进程间通信—无名管道通信
Posted 朱果果
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了进程间通信—无名管道通信相关的知识,希望对你有一定的参考价值。
进程间通信——Interprocess communication——IPC
每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程A把数据从用户空间拷到内核缓冲区,进程B再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信。
不同进程间的通信本质:进程之间可看到一份公共资源;而提供这份资源的形式或者提供者不同,造成了通信方式不同。
Linux下进程通信方式主要有以下几种:
1、管道---pipe
无名管道:可用于具有亲缘关系进程间的通信(例,fork函数建立父子间通信);
有名管道:除有与无名管道相同功能外,还允许无亲缘关系进程通信;
2、信号---signal
信号是在软件层上对中断机制的模拟,较为复杂,用于通知进程某事发生。
3、消息队列---message queue
消息队列是消息的链接表,包括Posix消息队列system V消息队列。有读/写权限的进程可以向队列中添加/读走消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺点。
4、共享内存---shared memory
使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制,如信号量结合使用,来达到进程间的同步及互斥。
5、信号量---semaphore
要作为进程间以及同一进程不同线程之间的同步和互斥的手段。
6、套接口---Socket
使用更为广泛的进程间通信机制,可用于网络中不同主机之间的进程通信。
管道实现机制:
管道是由内核管理的一个缓冲区,相当于我们放入内存中的一个纸条。管道的一端连接一个进程的输出。这个进程会向管道中放入信息。管道的另一端连接一个进程的输入,这个进程取出被放入管道的信息。一个缓冲区不需要很大,它被设计成为环形的数据结构,以便管道可以被循环利用。当管道中没有信息的话,从管道中读取的进程会等待,直到另一端的进程放入信息。当管道被放满信息的时候,尝试放入信息的进程会等待,直到另一端的进程取出信息。当两个进程都终结的时候,管道也自动消失。
一、无名管道
管道通信会把一个程序的输出直接连接到另外一个程序的输入。
特点:亲缘关系间通信,单工通信,有固定的读端与写端。
1、无名管道的创建与关闭
#include <unistd.h> int pipe (int pfd[2]); //成功返回0,错误返回-1
无名管道由调用pipe函数来创建,是基于文件描述符的通信方式。pfd包含两个元素的整形数组,存放文件描述符。pfd[0]用于读管道, pfd[1]用于写管道。
管道关闭时只需要close()函数关闭两文件描述符即可。
2、实现管道通信
(1)父进程创建管道,得到两个文件描述符指向管道的两端
(2)父进程fork出子进程,子进程也有两个文件描述符指向同一管道
(3)父进程关闭fd[0],关闭管道读端,可往管道写;子进程关闭fd[1],关闭管道写端,可从管道读。管道是用环形队列实现的,数据从写端写入,从读端读出,进而实现进程间通信。
1 #include <stdio.h> 2 #include <unistd.h> 3 #include <string.h> 4 #include <errno.h> 5 6 int main(void) 7 { 8 int pfd[2]; 9 pid_t pid; 10 int ret; 11 12 /*1.父进程创建管道,得到文件描述符指向管道两端*/ 13 ret = pipe(pfd); 14 if(ret == -1) 15 { 16 perror("pipe error\\n"); 17 return -1; 18 } 19 20 /* 2.父进程fork出子进程,子进程的描述符指向同一管道*/ 21 pid = fork(); 22 if(pid<0) 23 { 24 perror("fork error\\n"); 25 return -1; 26 }else if(pid == 0) //子进程关闭pfd[0]写端,ta可以从管道读 27 { 28 close(pfd[0]); 29 char buf[64] = "I am child process!\\n"; 30 while(1) 31 { 32 write(pfd[1],buf,strlen(buf)); 33 sleep(1); 34 } 35 }else //父进程关闭pfd[1]读端,他可以写入管道 36 { 37 close(pfd[1]); 38 char buf[64]; 39 while(1) 40 { 41 memset(buf,0,64); //初始化内存空间,防止打印乱码 42 ret = read(pfd[0],buf,64); 43 if(ret > 0) 44 { 45 printf("msg from child %s\\n",buf); 46 }else{ 47 break; 48 } 49 } 50 } 51 }
3.管道读取数据的四种情况
参考:https://blog.csdn.net/skyroben/article/details/71513385
4、获取管道容量大小
只要写端一直写,读端不读且不关闭fd[0]
1 #include <stdio.h> 2 #include <unistd.h> 3 #include <string.h> 4 #include <errno.h> 5 6 int main(void) 7 { 8 //pfd[0]用于读管道,pfd[1]用于写管道 9 int pfd[2]; 10 pid_t pid; 11 int ret; 12 int i; 13 14 ret = pipe(pfd); //创建管道 15 if(ret < 0) 16 { 17 perror("pipe error!\\n"); 18 return -1; 19 } 20 //一直写管道,知道堵塞 21 for(i=0; i<1000000; i++) 22 { 23 write(pfd[1],"a",1); 24 printf("i = %d\\n",i); 25 } 26 27 }
写到65535后就发生了管道阻塞,而65536为64K大小即管道容量。
以上是关于进程间通信—无名管道通信的主要内容,如果未能解决你的问题,请参考以下文章