进程间通信—无名管道通信

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 }
pipe_test.c

 

  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大小即管道容量。

 

 

 

 

 

 

 

 

 

 

以上是关于进程间通信—无名管道通信的主要内容,如果未能解决你的问题,请参考以下文章

进程间通信的方式

Linux系统编程进程间通信之无名管道

进程间通信(无名管道)

linux进程间通信之一:无名管道

多进程编程之进程间通信-管道和消息队列

进程间通信----无名管道