天天看点

进程间的通信方式(含代码)进程间通信方式的分类详解尾

进程间通信方式的分类

进程间通信方式总共可以分为七类,分别为有名管道,无名管道,信号,消息队列,共享内存,信号量,套接字(socket)。

1、有名管道FIFO:有名管道是一种半双工的通信方式,可以实现两个互不相关的进程之间的通信,并且在文件系统中可见,可以通过文件名找到。且遵循先进先出的原则,不支持lseek().

2、无名管道pipe:无名管道是一种半双工的通信方式,具有固定的读端fd[0]和写端fd[1],且只能用于具有亲缘关系的进程之间的通信,速度慢且容量有限;管道可以看成特殊的文件,读写可以使用文件IO中的read和write。

3、信号:信号 ( signal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。

4、消息队列MessageQueue:消息队列是一种全双工的通信方式,消息队列由消息队列ID来唯一标识,消息丢列就是一个消息列表,用户可以在消息队列中添加消息、读取消息等。消息队列可以按照类型来发送/接收消息,相同类型先入先出,不同类型随意存取。

5、共享内存SharedMemory:共享内存是一种最为高效的通信方式,进程可以直接读写内存,而不需要任何数据的拷贝。为了在多个进程间交换数据,内核专门留出了一块内存区,可以由需要访问的进程将其映射到自己的私有地址空间。进程可以直接读写这一内存区而不需要进行数据的拷贝,从而大大提高效率。由于多个进程共享一段内存,因此也需要某种同步机制,如互斥锁和信号量。

6、信号量Semaphore:一般与共享内存配合使用。信号量是一个计数器,可以用来控制多个进程对共享资源的访问。

7、套接字Socket:套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同主机之前的进程通信。

详解

有名管道

特点

1.只能用于具有亲缘关系(父子进程/兄弟进程)的进程之间的通信,速度慢,容量有限

2.半双工的通信方式,具有固定的读端fd[0]和写端fd[1].

3.管道可以看成一种特殊的文件,对于它的读写我们实用文件IO中read和write

相关函数

进程间的通信方式(含代码)进程间通信方式的分类详解尾

代码案例

实现从有名管道中读数据

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#define FIFO "myfifo"
int main(void)
{
	//1.创建有名管道
         //管道文件不存在则新建,存在,返回	
	if(mkfifo(FIFO,O_CREAT|0777)<0 && errno!=EEXIST)
	{
		perror("mkfifo error");
		return -1;
	}
	printf("mkfifo ok\n");

	//2.打开有名管道
	//以只读方式打开文件
	int fd=open(FIFO,O_RDONLY);
	if(fd<0)
	{
		perror("file open error");
		return -1;
	}
	//3.读数据
	char buf[20]={'\0'};

	while(1)
	{
		memset(buf,'\0',sizeof(buf));
		read(fd,buf,sizeof(buf));//1.从有名管道读取到缓冲区

		printf("read:%s",buf); //2.将缓冲区的内容显示在终端
		if(strncmp(buf,"exit",4)==0) 
			break;

	}
	//4.关闭管道
	close(fd);
	return 0;
}

           

实现从有名管道中写数据

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#define FIFO "myfifo"
int main(void)
{
	//1.创建有名管道
         //管道文件不存在则新建,存在,返回	
	if(mkfifo(FIFO,O_CREAT|0777)<0 && errno!=EEXIST)
	{
		perror("mkfifo error");
		return -1;
	}
	printf("mkfifo ok\n");

	//2.打开有名管道
	//以只写方式打开文件
	int fd=open(FIFO,O_WRONLY);
	if(fd<0)
	{
		perror("file open error");
		return -1;
	}
	//3.写入数据
	char buf[20]={'\0'};

	while(1)
	{
		printf("write:");
		fgets(buf,sizeof(buf),stdin); //1.从键盘写入到缓冲区
		
		write(fd,buf,strlen(buf)); //2.将缓冲区的内容写入到有名管道中
		if(strncmp(buf,"exit",4)==0) 
			break;

	}
	//4.关闭管道
	close(fd);
	return 0;
}

           

无名管道

特点

1.只能用于具有亲缘关系(父子进程/兄弟进程)的进程之间的通信,速度慢,容量有限

2.半双工的通信方式,具有固定的读端fd[0]和写端fd[1].

3.管道可以看成一种特殊的文件,对于它的读写我们实用文件IO中read和write

相关函数

进程间的通信方式(含代码)进程间通信方式的分类详解尾

代码案例

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>

int main(void)
{
	//1.创建一个无名管道
	//1-1.定义一个包含两个元素的整形数组
	int fd[2]={0};

	//1-2.创建无名管道
	if(pipe(fd)<0)
	{
		perror("pipe error");
		return -1;
	}

	//创建成功之后,就有了固定读端(fd[0])和写端(fd[1])
	//2.创建子进程
	pid_t pid;
	pid=fork();
	
	if(pid<0)
	{
		perror("fork error");
		return -1;
	}else if(pid==0)
	{
		//子进程  fd[0]读端   fd[1] 写端
		//子进程发送:
		//  1.关闭读端
		close(fd[0]);
		char buf[20]={'\0'};
		//  2.发送数据
		while(1)
		{
		//	printf("child:");
			fgets(buf,sizeof(buf),stdin);  //1.将数据从键盘输入
			write(fd[1],buf,strlen(buf));                       //2.将数据写入到无名管道中
		}
	}else if(pid>0)
	{
		//父进程 fd[0] 读端  fd[1]写端
		//1.关闭写端
		close(fd[1]);
		char buf[20]={'\0'};
		//2.读取数据
		while(1)
		{
			//1.从无名管道读取到buf
			//printf("father:");
			read(fd[0],buf,sizeof(buf));
			//2.从buf显示中标准输出
			printf("father:%s",buf);
		}
	}
}

           

信号

特点

信号是一种异步通信方式。

信号产生的方式

1、硬件产生(组合键)信号

以下三种均为硬件产生的信号

ctrl+c <>SIGINT

ctrl+z <>SIGTSTP

ctrl+\ <==>SIGQUIT

2、内核发送信号

相关函数举例

alarm()也称为闹钟函数,它可以在进程中设置一个定时器。当定时器指定的时间到时,内核就向进程发送SIGALRM信号。

进程间的通信方式(含代码)进程间通信方式的分类详解尾

代码实现

#include <stdio.h>
#include <unistd.h>
int main(void)
{

	alarm(5); //设置了闹钟函数,时间到了之后,
		 //内核给进程发送SIGALRM信号,收到该信号后进程结束
	while(1)
	{
		printf("hello world\n");
		sleep(1);
	}
	return 0;
}

           

pause()函数是用于将调用进程挂起直到收到信号为止。

进程间的通信方式(含代码)进程间通信方式的分类详解尾

代码实现

#include <stdio.h>
#include <unistd.h>
int main(void)
{

	alarm(5); //设置了闹钟函数,时间到了之后,
		 //内核给进程发送SIGALRM信号,收到该信号后进程结束
	pause(); //将进程挂起,直到受到某个信号为止
	while(1)
	{
		printf("hello world\n");
		sleep(1);
	}
	return 0;
}


           

3、软件方式产生信号

相关函数

raise() 自己给自己发送信号

进程间的通信方式(含代码)进程间通信方式的分类详解尾

kill() 给别人发送信号

进程间的通信方式(含代码)进程间通信方式的分类详解尾
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>

int main(void)
{
	//1.创建子进程
	pid_t pid;

	pid=fork();

	if(pid<0)
	{
		perror("fork error");
		return -1;
	}else if(pid==0)
	{
		int i=1;
		while(1)
		{
			if(i%13==0)
			{
				printf("子进程被自己停止\n");
				raise(SIGSTOP); //当i是13.发送一个停止的信号
			}

			printf("recv cam\n");
			sleep(1);
			i++;
		}
	}else if(pid>0)
	{
		int j=1;
		while(1)
		{
			if(j%17==0)
			{
				printf("父进程启动子进程\n");
				kill(pid,SIGCONT);  //给别人发送信号
			}
			printf("send data\n");
			sleep(1);
			j++;
		}
	}
	return 0;
}

           

捕获信号

1.执行默认操作

2.忽略信号 即对信号不做任何处理,有两个信号不能忽略 SIGKILL SIGSTOP

相关函数

进程间的通信方式(含代码)进程间通信方式的分类详解尾
#include <stdio.h>
#include <signal.h>
#include <unistd.h>

int main(void)
{


	//捕获信号,捕获到信号之后干什么忽略
	//参数1:信号
	//参数2:SIG_IGN 忽略信号
	signal(SIGINT,SIG_IGN); //ctrl+c
	signal(SIGTSTP,SIG_IGN);//ctrl+z
	signal(SIGQUIT,SIG_IGN);//ctrl+'\\'
	signal(SIGALRM,SIG_IGN);

	alarm(5);
	while(1)
	{
		printf("helloworld\n");
		sleep(1);
	}
	return 0;
}

           

3.安装信号,获得信号之后,我们要去执行自定义函数

进程间的通信方式(含代码)进程间通信方式的分类详解尾

代码实现

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
//void (int) 函数
void handler(int sig)
{
	switch(sig)
	{
		case SIGINT:
			printf("I am ctrl+c\n");
			break;
		case SIGTSTP:
			printf("I am ctrl+z\n");
			break;
		case SIGQUIT:
			printf("I am ctrl+\\\n");
			break;
		case SIGALRM:
			printf("I am alarm\n");
			break;
	}
}
int main(void)
{


	//捕获信号,捕获到信号之后执行自定义函数   void (int)
	//参数1:信号
	//参数2:SIG_IGN 忽略信号  handler    void (int) 函数指针
	signal(SIGINT,handler); //ctrl+c
	signal(SIGTSTP,handler);//ctrl+z
	signal(SIGQUIT,handler);//ctrl+'\\'
	signal(SIGALRM,handler);

	alarm(5);
	while(1)
	{
		printf("helloworld\n");
		sleep(1);
	}
	return 0;
}

           

消息队列

特点

1.消息队列由消息队列ID来唯一标识

2.消息队列就是一个消息的列表。用户可以在消息队列中添加消息、读取消息等。

3.消息队列可以按照类型来发送/接收消息,相同类型先入先出,不同类型随意存取。

4.全双工的一个通信方式

进程间的通信方式(含代码)进程间通信方式的分类详解尾

相关函数

创建消息队列

进程间的通信方式(含代码)进程间通信方式的分类详解尾

发送接收消息队列

进程间的通信方式(含代码)进程间通信方式的分类详解尾
进程间的通信方式(含代码)进程间通信方式的分类详解尾

删除消息队列

进程间的通信方式(含代码)进程间通信方式的分类详解尾
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
	//1.获得key值
	key_t key;
	//参数1:文件路径名  一般使用当前路径./
	//参数2:一个int  随机数
	key=ftok("./",12);
	//2.创建消息队列  msgget
	//参数1:key值  参数2:创建权限
	system("ipcs  -q");
	sleep(5);
	int msgid=msgget(key,IPC_CREAT|0777);
	if(msgid<0)
	{
		perror("msgget error");
		return -1;
	}
	printf("------------------------------------------------\n");
	system("ipcs -q");
	sleep(5);
	// 创建共享内存   shmget
	// 创建信号灯集   semget
	//3.删除消息队列
	//msgctl(msgid,IPC_RMID,NULL);
	printf("================================================\n");
	system("ipcs -q");
	sleep(5);
	return 0;
}

           

执行结果

进程间的通信方式(含代码)进程间通信方式的分类详解尾

利用消息队列实现进程间通信的代码

发送端

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <unistd.h>
#include "msg.h"
#include <string.h>
int main(void)
{
	//1.获得key值
	key_t key;
	//参数1:文件路径名  一般使用当前路径./
	//参数2:一个int  随机数
	key=ftok("./",12);
	//2.创建消息队列  msgget
	//参数1:key值  参数2:创建权限
	int msgid=msgget(key,IPC_CREAT|0777);
	if(msgid<0)
	{
		perror("msgget error");
		return -1;
	}


	//3.发送消息
	//3.1.定义一个消息结构变量
	Mbuf sndbuf;
	sndbuf.mtype=100; //设置消息类型是100的

	while(1){
		printf("send:\n");
		fgets(sndbuf.mtext,sizeof(sndbuf.mtext),stdin);
		//3.2.发送给接受端
		//参数1:msgID值
		//参数2:  msgsnd(msgid,&sndbuf,sizeof(sndbuf.mtext),0);
		//参数3:正文的大小
		//参数4:阻塞发送 0
		msgsnd(msgid,&sndbuf,strlen(sndbuf.mtext),0);
	}

	// 创建共享内存   shmget
	// 创建信号灯集   semget
	//3.删除消息队列
	msgctl(msgid,IPC_RMID,NULL);
	return 0;
}

           

接收端

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <unistd.h>
#include "msg.h"
#include <string.h>
int main(void)
{
	//1.获得key值
	key_t key;
	//参数1:文件路径名  一般使用当前路径./
	//参数2:一个int  随机数
	key=ftok("./",12);
	//2.创建消息队列  msgget
	//参数1:key值  参数2:创建权限
	int msgid=msgget(key,IPC_CREAT|0777);
	if(msgid<0)
	{
		perror("msgget error");
		return -1;
	}


	//3.按照类型来接受消息
	Mbuf recBuf;
	recBuf.mtype=100;

	while(1){
		memset(&recBuf,'\0',sizeof(recBuf));
		//参数1:msgid
		//参数2:接受消息的缓冲区
		//参数3:接受消息的字节数
		//参数4:接受消息的类型
		//参数5:阻塞接受
		msgrcv(msgid,&recBuf,sizeof(recBuf.mtext),recBuf.mtype,0);
		printf("read:%s\n",recBuf.mtext);
	}

	//
	msgctl(msgid,IPC_RMID,NULL);
	return 0;
}
           

共享内存

特点

1.共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,而不需要任何数据的拷贝。

2.为了在多个进程间交换信息,内核专门留出了一块内存区,可以由需要访问的进程将其映射到自己的私有地址空间。

3.进程就可以直接读写这一内存区而不需要进行数据的拷贝,从而大大提高的效率。

4.由于多个进程共享一段内存,因此也需要依靠某种同步机制,如互斥锁和信号量等。

相关函数

1、创建共享内存

进程间的通信方式(含代码)进程间通信方式的分类详解尾

2、映射共享内存

即把指定的共享内存映射到进程的地址空间用于访问

进程间的通信方式(含代码)进程间通信方式的分类详解尾

3.向共享内存写入数据

4.撤销映射

进程间的通信方式(含代码)进程间通信方式的分类详解尾

5.删除共享内存

进程间的通信方式(含代码)进程间通信方式的分类详解尾

代码实现

利用共享内存的方式实现进程间通信

发送端

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <stdlib.h>
int main(void)
{
	//1.获得key值
	key_t key;
	key=ftok("./",12);

	//	system("ipcs -m");
	//	sleep(5);
	//2.创建共享内存
	//参数1:ftok()获得的key值
	//参数2:空间的大小 
	//参数3:不存在IPC_CREAT | 0777
	//IPC_EXCL

	int shmid=shmget(key,1024,IPC_CREAT|0600);
	if(shmid<0)
	{
		perror("shmget error");
		return -1;
	}
	printf("--------------------------------------------\n");
	//	system("ipcs -m");
	//	sleep(5);

	//3.映射共享内存区域,并返回映射后的首地址
	char * pShm=NULL;
	pShm=(char *)shmat(shmid,NULL,0);
	if(pShm==NULL)
	{
		perror("shmat error");
		return -1;
	}

	while(1){
		printf("write:");
		//4.直接给映射后的地址(共享内存)写入数据
		fgets(pShm,1024,stdin);
	}
	//	system("ipcs -m");
	//	sleep(5);

	//5.撤销共享内存
	shmdt(pShm);
	//6.删除共享内存
	//shmctl(shmid,IPC_RMID,NULL);

}

           

接收端

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <stdlib.h>
int main(void)
{
	//1.获得key值
	key_t key;
	key=ftok("./",12);

	//system("ipcs -m");
	//sleep(5);
	//2.创建共享内存
	//参数1:ftok()获得的key值
	//参数2:空间的大小 
	//参数3:不存在IPC_CREAT | 0777
	//IPC_EXCL

	int shmid=shmget(key,1024,IPC_CREAT|0600);
	if(shmid<0)
	{
		perror("shmget error");
		return -1;
	}
	//printf("--------------------------------------------\n");
	//system("ipcs -m");
	//sleep(5);

	//3.映射共享内存区域,并返回映射后的首地址
	char * pShm=NULL;
	pShm=(char *)shmat(shmid,NULL,0);
	if(pShm==NULL)
	{
		perror("shmat error");
		return -1;
	}

	while(1){
		//4.从映射的空间内读取数据
		printf("%s",pShm);
	}
	//system("ipcs -m");
	//sleep(5);

	//5.撤销共享内存
	shmdt(pShm);
	//6.删除共享内存
	//	shmctl(shmid,IPC_RMID,NULL);

}

           

信号量

特点

信号量不能传递数据,一般和共享内存配合使用,实现共享内存同步。

无名信号量

sem_t sem1 信号量是共享的

sem_init() 初始化信号量

sem_wait() P操作 sem>0 -1操作 ==0 阻塞线程

sem_post() V操作 +1操作,同时唤醒阻塞的线程

有名信号量

相关函数

无名信号量

sem_t sem1 信号量是共享的

sem_init() 初始化信号量

sem_wait() P操作 sem>0 -1操作 ==0 阻塞线程

sem_post() V操作 +1操作,同时唤醒阻塞的线程

有名信号量

进程间的通信方式(含代码)进程间通信方式的分类详解尾

sem_wait (P操作)

Sem_post(V操作)

进程间的通信方式(含代码)进程间通信方式的分类详解尾

代码实现

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>

int main(void)
{
	//1.创建信号量1和2
	//参数1:名字
	//参数2:O_CREAT 不存在新建,存在返回
	//参数3:权限 八进制
	//参数2:信号量值
	//返回值:信号量指针
	sem_t * psem1=sem_open("aaa",O_CREAT,0666,1);
	sem_t * psem2=sem_open("bbb",O_CREAT,0666,0);

	while(1)
	{
		sem_wait(psem1);  //P操作
		printf("hello\n");
		sleep(1);
		sem_post(psem2); //V操作
	}
}

           
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>

int main(void)
{
	//1.创建信号量1和2
	//参数1:名字
	//参数2:O_CREAT 不存在新建,存在返回
	//参数3:权限 八进制
	//参数2:信号量值
	//返回值:信号量指针
	sem_t * psem1=sem_open("aaa",O_CREAT,0666,1);
	sem_t * psem2=sem_open("bbb",O_CREAT,0666,0);

	while(1)
	{
		sem_wait(psem1);  //P操作
		printf("hello\n");
		sleep(1);
		sem_post(psem2); //V操作
	}
}

           

套接字(socket)

由于此套接字为网络编程的内容,后期会更新相关知识,再次不做解释,忘谅解。

以上为自己对进程间通信方式内容的理解,有不足之处还请大佬们在评论区指正出来,我后期改正。