当前位置:   article > 正文

Linux操作系统应用程序设计——五种进程间的通信方式

Linux操作系统应用程序设计——五种进程间的通信方式


前言

1、通常来说进程间的通信方式共有六种:
(1)管道通信 ——无名管道 (pipe)、有名管道 (fifo)
(2)信号通信 (sinal)
(3)信号量 (Semaphore)
(4)共享内存 (SharedMemory)
(5)消息队列 (MessageQueue)
(6)套接字 (Socket)
但是本文只介绍前五种,你肯定疑惑了,第六种为什么不说?
那原因也很简单,我觉得文章太长了!


一、管道通信

1、无名管道

1、无名管道的特点:
(1)只能用于具有亲缘关系的进程之间的通信
(2)是一个单工的通信模式,具有固定的读端和写端
(3)管道也可以看成一种特殊文件,对他的读写可以使用read(),write()函数
2、原理示意图:
在这里插入图片描述
3、pipe函数
功能:创建无名管道

头文件:
#include<unistd.h>
函数原型:
int pipe(int fd[]);
参数:
fd[]:包含两个元素的整型数组,存放读端和写端的文件描述符
例子:
#include <stdio.h>
#include <unistd.h>
int main()
{
	int fd[2];		
	pipe(fd);		//传出两个文件描述符  fd[0]    fd[1]
	int pid = fork();
	if(pid == 0)//子进程
	{	
		close(fd[0]);	//子进程关闭读端
		write(fd[1], "hello", sizeof("hello"));
	}
	else if(pid > 0)//父进程
	{
		char buf[100] = { 0 };
		close(fd[1]);//父进程关闭写端
		wait(NULL);//等待子进程结束
		read(fd[0], buf, sizeof(buf));//读出数据	
		printf("read is %s\n", buf);//打印
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

2、有名管道

1、有名管道的特点:
(1)可以使两个不相关的进程实现彼此通讯
(2)该管道可以通过路径名指出,在文件系统中可见
(3)严格遵守先进先出的规则,在开始处读数据,末尾处写数据
2、原理示意图:
在这里插入图片描述
3、mkfifo函数
功能:创建有名管道

头文件:
#include<sys/types.h>
#include<sys/state.h>
函数原型:
int mkfifo(const char *filename,mode_t mode);
参数:
filename:要创建的管道名
mode:管道的访问权限
例子:
//读端
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main()
{
	mkfifo("myfifo", 0666);		
	int fd = open("myfifo", O_RDWR);
	if(fd > 0)
	{
		char buf[100] = { 0 };
		read(fd, buf, sizeof(buf));
		printf("read is %s\n", buf);
		close(fd);
	}
}
//写端
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main()
{
	int fd = open("myfifo", O_RDWR);
	if(fd > 0)
	{
		write(fd, "hello", sizeof("hello"));
		close(fd);
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

二、信号通信

1、信号是进程间通信机制中唯一的异步通讯机制
2、进程可以通过三种方式来响应信号
(1)忽略信号:对信号不做如何处理
(2)捕捉信号:定义信号处理函数,信号发生时,执行相应的处理函数
(3)执行默认操作:常见的信号如下
· SIGHUP:从终端上发出的结束信号。
· SIGINT:来自键盘的中断信号,即Ctrl+c。
· SIGKILL:该信号结束接收信号的进程。
· SIGCHLD:标识子进程停止或结束的信号。
· SIGSTOP:来自键盘Ctrl+z,或调试程序的停止执行信号。
· SIGQUIT:来自键盘的退出信号,即Ctrl+\。
· SIGALARM:进程的定时器到期,发送该信号。
3、kill函数
功能:发送信号给进程

头文件:
#include<signal.h>
#include<sys/types.h>
函数原型:
int kill(pid_t pid,int sig);
参数:
pid:发送信号给进程号为pid的进程
sig:信号类型
例子:
#include <signal.h>
int main()
{
	kill(11111, SIGINT);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

4、signal函数
功能:定义一个函数,当产生某个信号时,自动执行此函数

头文件:
#include<signal.h>
函数原型:
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum,sighandler_t handler);
参数:
signum:指定信号代码
handler:SIG_IGN 忽略该信号
		SIG_DFL 采用系统默认方式处理信号
		自定义的信号处理函数
例子:
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
void *fun(int n)		//n 当产生某个信号时,信号值
{
	printf("recv a signal CTRL+C %d\n", n);
	exit(0);
}
int main()
{
	signal(SIGINT, fun);	
	while(1);		//不让程序结束	
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

5、alarm函数
功能:在进程中设定一个定时器,当定时器时间到了,发送一个SIGALARM信号给进程

头文件:
#include<unistd.h>
函数原型:
unsigned int alarm(unsigned int seconds);
参数:
seconds:指定秒数
例子:
//发送信号
#include <stdio.h>
int main()
{
	alarm(2);
	while(1)
	{
		printf("hello world\n");
		sleep(1);
	}
}
//接收信号
#include <stdio.h>
#include <signal.h>
void fun(int n)
{
	printf("recv a signal SIGALRM\n");
}
int main()
{
	alarm(1);	//1秒钟之后产生SIGALRM信号
	signal(SIGALARM, fun);	//捕获SIGALRM信号,执行fun
	while(1);	//不让程序暂停
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

三、信号量

那个,这个暂时先放一放。

四、共享内存

1、共享内存是一种最为高效的进程间通信方式:因为进程可以直接读写内存,不需要任何数据的复制
2、原理图:
在这里插入图片描述
3、shmget函数
功能:创建共享内存

头文件:
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
函数原型:
int shmget(key_t key,int size,int shmflg);
参数:
key:共享内存的键值
size:共享内存区的大小
shmflg:共享内存的权限,可以使用八进制表示法
返回值:
共享内存的标识符
例子:
#include <sys/shm.h>
#include <stdio.h>
int main()
{
	int shmid = shmget(55555, 10 * sizeof(int), 0666 | IPC_CREAT);//IPC_CREAT不存在就新建
	printf("shmid is %d\n", shmid);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

4、shmat函数
功能:映射共享内存

头文件:
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
函数原型:
char *shmat(int shmid,const void *shmaddr,int shmflg);
参数:
shmid:要映射的共享内存区标识符
shmaddr:将共享内存映射到指定地址(若为0,表示系统自动分配地址,并把该段共享内存映射到调用进程的地址空间)
shmflg:共享内存的权限,可以使用八进制表示法
返回值:
被映射的段地址
例子:
int *p = shmat(shmid, NULL, 0);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

5、shmdt函数
功能:撤销映射

头文件:
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
函数原型:
int shmdt(const void *shmaddr);
参数:
shmaddr:被映射的共享内存段地址
例子:
#include <sys/shm.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
	int shmid = shmget(IPC_PRIVATE, 10 * sizeof(int), 0644);
	printf("shmid %d\n", shmid);
	int i;
	int pid = fork();		//创建子进程
	if(pid == 0)			//子进程在运行
	{
		//写共享内存
		int *p = shmat(shmid, NULL, 0);	//映射
		for(i = 0; i < 10; i++)
		{
			p[i] = i;		//写入共享内存
		}	
		shmdt(p);			//解除映射
		exit(0);			//子进程结束
	}
	else if(pid > 0)
	{
		//读共享内存
		wait(NULL);			//等子进程结束
		int *p = shmat(shmid, NULL, 0);	//映射
		for(i = 0; i < 10; i++)
		{
			printf("%d\n", p[i]);	//读出
		}	
		shmdt(p);			//解除映射
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

五、消息队列

1、消息队列就是一些消息的列表,可以实现消息的随机查询,这些消息存在于内核中,有队列ID来标识。
2、msgget函数
功能:

头文件:
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
函数原型:
int msgget(key_t key,int msgflg);
参数:
key:消息队列的键值
msgflg:权限标志位
返回值:
消息队列ID
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

3、msgsnd函数
功能:

#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
函数原型:
int msgsnd(int msqid,const void *msgp,size_t msgsz,int msgflg);
参数:
msqid:消息队列的队列ID
msgp:指向消息结构的指针
struct msgbuf
{
	long mtype;//消息类型
	char mtext[100];//消息正文
}
msgsz:消息正文的字节数
msgflg:IPC_NOWAIT 消息无法发送函数立即返回
		0 调用阻塞直到发送成功
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

4、msgrcv函数
功能:

#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
函数原型:
int msgrcv(int msqid,const void *msgp,size_t msgsz,long int msgtyp,int msgflg);
参数:
msqid:消息队列的队列ID
msgp:消息缓冲区
struct msgbuf
{
	long mtype;//消息类型
	char mtext[100];//消息正文
}
msgsz:消息正文的字节数
msgtyp:0 接收消息队列第一个消息
		大于0 接收消息队列第一个类型为msgtyp的消息
		小于0 接收消息队列中第一个类型值不小于msgtyp绝对值且类型值又最小的消息
msgflg:IPC_NOWAIT 若消息队列没有对应类型的消息,函数立即返回
		0 调用阻塞直到接收成功
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

5、msgctl函数
功能:删除队列消息

#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
函数原型:
int msgctl(int msqid,int cmd,struct msqid_ds *buf);
参数:
msqid:消息队列的队列ID
cmd:IPC_RMID 从系统内核中删除消息队列
buf:通常为NULL
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

6、例子

//发送消息
#include <sys/msg.h>
#include <stdio.h>
#include <string.h>
struct msgbuf
{
	long mtype;
	char mtext[100];
};

int main(int argc, char *argv[])
{
	if(argc < 3)
	{
		printf("param error, like this: ./send 1 hello\n");
		return -1;
	}
	struct msgbuf a;
	a.mtype = atoi(argv[1]);//接收消息类型
	strcpy(a.mtext, argv[2]);//接收消息正文
	int msgid = msgget(11111, 0666 | IPC_CREAT);//创建消息队列
	msgsnd(msgid, &a, sizeof(a) - sizeof(long), 0);//发送消息
}
//接收消息 
#include <sys/msg.h>
#include <sys/ipc.h>
#include <stdio.h>
struct msgbuf
{
	long mtype;
	char mtext[100];
};

int main(int argc, char *argv[])
{
	struct msgbuf b;//定义结构体变量
	if(argc < 2)
	{
		printf("param error, like this: ./recv 1\n");
	}
	int msgid = msgget(11111, 0666 | IPC_CREAT);//创建消息队列
	msgrcv(msgid, &b, sizeof(b) - sizeof(long), atoi(argv[1]), 0);//接收消息正文
	printf("msg type is %lu, buf is %s\n", b.mtype, b.mtext);//打印
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

总结

总结不动了!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/273117
推荐阅读
相关标签
  

闽ICP备14008679号