3.1 进程间通信之管道

一、引言

管道作用于有血缘关系的进程间的通信,完成数据传递。实际为内核使用环形队列机制,借助内核缓冲区(4k)实现。有如下特质:

1) 其本质是一个伪文件(实为内核缓冲区)

2) 由两个文件描述符引用,一个表示读端,一个表示写端。可定义一个文件描述符数组,存取。

3) 规定数据从管道的写端流入管道,从读端流出。

4) 数据自己读不能自己写,数据一旦被读走,便不在管道中存在,不可反复读取。 由于管道采用半双工通信方式。因此,数据也只能在一个方向上流动。

5) 只能在有公共祖先的(血缘关系的)进程间使用管道。

二、pipe函数

调用pipe系统函数即可创建一个管道,其函数原型为:

int pipe(int fd[2]);
成功:
0;失败:-1,设置errno

向管道文件读写数据其实是在读写内核缓冲区,函数参数数组包含pipe使用的两个文件的描述符:fd[0] → r; fd[1] → w,就像0对应标准输入,1对应标准输出一样。注意:在pipe使用时,无需open,但需手动close

管道方法通信流程:

1) 父进程调用pipe函数创建管道,得到两个文件描述符fd[0]fd[1]指向管道的读端和写端。

2) 父进程调用fork创建子进程,那么子进程也有两个文件描述符指向同一管道。

3) 父进程关闭管道读端,子进程关闭管道写端。父进程可以向管道中写入数据,子进程将管道中的数据读出。由于管道是利用环形队列实现的,数据从写端流入管道,从读端流出,这样就实现了进程间通信。

三、例程

 1 #include <unistd.h>
 2 #include <string.h>
 3 #include <stdlib.h>
 4 #include <stdio.h>
 5 #include <sys/wait.h>
 6 
 7 int main(void)
 8 {
 9     pid_t pid;
10     char buf1[1024];
11     int fd[2];
12     int len;
13     char *buf2 = "hello world
";
14     
15    if (pipe(fd) == -1) //创建管道 
16        printf("pipe error
");
17 
18    pid = fork(); //创建进程
19    if (pid < 0) {
20        printf("fork error
");
21    } 
22    else if (pid == 0) { 
23         close(fd[1]);  //子进程关闭写端
24         len = read(fd[0], buf1, sizeof(buf));//读取管道数据到buf中
25         write(STDOUT_FILENO, buf1, len);//将buf数据写入标准输出
26         close(fd[0]); //注意要close
27        } 
28     else {
29               close(fd[0]);//父进程关闭读端
30                  write(fd[1], buf2, strlen(buf2));//向管带的写端写入字符串p
31           wait(NULL);  //等待子进程释放
32           close(fd[1]);
33    }
34     
35     return 0;
36 }

编译执行结果:

补充:

读管道:

1)管道中有数据,read返回实际读到的字节数。

2)管道中无数据:

(1) 管道写端被全部关闭,read返回0 (好像读到文件结尾)

(2) 写端没有全部被关闭,read阻塞等待(不久的将来可能有数据递达,此时会让出cpu)

写管道:

1)管道读端全部被关闭, 进程异常终止(也可使用捕捉SIGPIPE信号,使进程不终止)

2)管道读端没有全部关闭:

(1) 管道已满,write阻塞。

(2) 管道未满,write将数据写入,并返回实际写入的字节数。

缓冲大小:使用ulimit –a 命令来查看当前系统中创建管道文件所对应的内核缓冲区大小

原文地址:https://www.cnblogs.com/lxl-lennie/p/10237740.html