Unix进程间的通信方式——无名管道pipe-学习笔记

一、何为进程间通信

        进程间通信(IPC,InterProcess Communication),是指在不同进程之间传播或交换信息。简单说就是进程之间可以相互发送数据。

        IPC的方式通常有无名管道(PIPE)有名管道(FIFO)信号(Signal)共享内存(Share Memory)消息队列(Message Queue)信号灯集(Semaphore Set)套接字(Socket)Streams等。其中 Socket和Streams支持不同主机上的两个进程IPC。Socket用在网络编程中。

二、无名管道

 

  管道通常指无名管道,是 UNIX 系统IPC最古老的形式。
  特点有:
    1、它是半双工的(即数据只能在一个方向上流动,单向),具有固定的读端和写端。
    2、它只能用于具有亲缘关系的进程之间的通信(也是父子进程或者兄弟进程之间),实现依赖父子进程文件共享。
    3、它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write 等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。

 

    4、pipe时,写端存在,有数据则读,无则阻塞pipe时,写端不存在,不能有数据,读则立刻返回

 

    5、pipe时,读端存在,有空间则成功写,无空间则阻塞pipe时,读端不存在,有空间则管道破裂,无空间不存在

 

    6、写管道上限是65536

  函数原型:

1 #include <unistd.h>
2 int pipe(int pipefd[2]);

 

  由参数pipefd[2]返回两个文件描述符:filedes[0]为读而打开,filedes[1]为写而打开。filedes[1]的输出是filedes[0]的输入。

  单个进程中的管道几乎没有任何用处。通常,调用pipe的进程接着调用fork,这样就创建了从父进程到子进程(或反向)的IPC通道。

  父子进程都有读端和写端,子进程的是从父进程复制过来的。

  进程复制的时候复制了PCB、文件结构体,不止拷贝了文件描述符。

                         

观察单进程半双工管道的两种办法

 

调用fork之后的半双工管道

  调用fork之后做什么取决于我们想要有的数据流的方向。例如,对于从父进程到子进程的管道,父进程关闭管道的读端(fd[0]),子进程则关闭写端(fd[1])。

从父进程到子进程管道

  举例:

 1 /*例1:只有一个进程,读写管道文件*/
 2 #include <stdio.h>
 3 #include <stdlib.h>
 4 #include <assert.h>
 5 #include <string.h>
 6 #include <unistd.h>
 7 
 8 int main()
 9 {
10     int fd[2];
11     pipe(fd);
12 
13     write(fd[1],"hello world",12);
14 
15     sleep(2);
16 
17     char buff[128];
18     int n = read(fd[0],buff,127);
19     printf("read:%s\n",buff);
20 
21     close(fd[0]);
22     close(fd[1]);
23 }
 1 /*例2:创建由父进程到子进程的管道,父进程写入helloworld,子进程读取数据到buf,然后打印输出。*/
 2 #include <stdio.h>
 3 #include <stdlib.h>
 4 #include <string.h>
 5 #include <assert.h>
 6 #include <unistd.h>
 7 
 8 int main()
 9 {
10     int fd[2];
11     pipe(fd);//fd[0]是读,fd[1]是写
12     if(fork() != 0)
13     {
14         close(fd[0]);
15         write(fd[1],"helloworld",10);
16     }
17     else
18     {
19         close(fd[1]);
20         char buf[128] = {0};
21         read(fd[0],buf,127);
22         printf("%s\n",buf);
23     }
24     return 0;
25 }
 1 /*例3:使用管道文件,创建由父进程到子进程的管道,父进程循环输入数据,子进程循环读取数据,当写端输入end时,父子线程都结束。*/
 2 #include <stdio.h>
 3 #include <string.h>
 4 #include <unistd.h>
 5 #include <stdlib.h>
 6 #include <assert.h>
 7 
 8 int main()
 9 {
10     int fd[2];
11     pipe(fd);
12 
13     pid_t pid = fork();
14 
15     if(pid == 0)
16     {
17         close(fd[1]);
18         char buff[128] = {0};
19         int n = 0;
20         while((n = read(fd[0],buff,127)) > 0)
21         {
22             printf("child read:%s\n",buff);
23         }
24         close(fd[0]);
25     }
26     else
27     {
28         close(fd[0]);
29         while(1)
30         {
31             char buff[128] = {0};
32             printf("input:\n");
33             fgets(buff,128,stdin);
34             if(strncmp(buff,"end",3)==0)
35             {
36                 break;
37             }
38             write(fd[1],buff,127);
39         }
40         close(fd[1]);
41     }
42     exit(0);
43 }

  注意:
  (1)当读一个写端已被关闭的管道是,在所有数据都被读取后,read返回0,以指示达到文件结束处。管道的写端彻底关闭(父子进程的写端都得关闭,否则会有进程处于未关闭状态,还在等待写),读端返回一个0,父子进程都得关闭。
  (2)如果写一个读端已被关闭的管道,则产生信号SIGPIPE。如果忽略该信号或者捕捉该信号并从处理程序返回,则write返回-1,errno设置为EPIPE。

posted @ 2020-12-14 23:57  涅风  阅读(626)  评论(0编辑  收藏  举报