pipe() —进程通信
1. 管道的概念
从概念上讲,管道是两个进程之间的连接,一个进程的标准输出成为另一个进程的标准输入。在UNIX操作系统中,管道用于进程间通信。
- 如果在某个内容写入管道之前,某个进程试图读取该内容,则该进程将挂起,直到内容被写入。即如果没有数据会阻塞住
- 0对应标准输入,1对应标准输出
- 管道可以被创建进程及其所有子进程读写。一个进程可以写入这个“虚拟文件”或管道,另一个相关进程可以从中读取。
- 管道通信是FIFO的队列
- 父子进程共享管道:当我们在任何进程中使用fork时,文件描述符在子进程和父进程之间保持打开状态。如果我们在创建管道后调用fork,则父级和子级可以通过管道进行通信。
本次重点:可以利用管道使得父子进程交替执行,但是需要合理地设计程序,不能一股脑的write,需要像互斥锁一样,让一个进程读完,或者写完之后需要等待另外一个进程的写或读才能进行接下去的操作,确实,那我懂了
2. 管道的限制
- 数据自己读不能自己写
- 它们是半双工的。数据只能在一个方向上流动。
- 数据一旦被读走,便不在管道中存在,不可反复读取。
- 它们只能在具有公共祖先的进程之间使用。通常,一个管道由一个进程创建,然后该进程调用fork,此后父、子进程之间就可应用该管道。
3. 管道读端与写端的close()操作
管道的读写行为
如果所有指向管道写端的文件描述符都关闭了(管道写端引用计数为0),而仍然有进程从管道的读端读数据,那么管道中剩余的数据都被读取后,再次read会返回0,就像读到文件末尾一样。
如果有指向管道写端的文件描述符没关闭(管道写端引用计数大于0),而持有管道写端的进程也没有向管道中写数据,这时有进程从管道读端读数据,那么管道中剩余的数据都被读取后,再次read会阻塞,直到管道中有数据可读了才读取数据并返回。
如果所有指向管道读端的文件描述符都关闭了(管道读端引用计数为0),这时有进程向管道的写端write,那么该进程会收到信号SIGPIPE,通常会导致进程异常终止。当然也可以对SIGPIPE信号实施捕捉,不终止进程。具体方法信号章节详细介绍。
如果有指向管道读端的文件描述符没关闭(管道读端引用计数大于0),而持有管道读端的进程也没有从管道中读数据,这时有进程向管道写端写数据,那么在管道被写满时再次write会阻塞,直到管道中有空位置了才写入数据并返回。
引用自https://blog.csdn.net/readlnh/article/details/102762315
4. 例子
实现一个父子进程协作将变量x从1加到10
/*
* Filename : ppipe.c
* copyright : (C) 2006 by zhanghonglie
* Function : 利用管道实现在父子进程间传递整数
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int pid; //进程号
int pipe1[2]; //存放第一个无名管道标号
int pipe2[2]; //存放第二个无名管道标号
int x; // 存放要传递的整数
//使用 pipe()系统调用建立两个无名管道。建立不成功程序退出,执行终止
if(pipe(pipe1) < 0){
perror("pipe not create");
exit(EXIT_FAILURE);
}
if(pipe(pipe2) < 0){
perror("pipe not create");
exit(EXIT_FAILURE);
}
//使用 fork()系统调用建立子进程,建立不成功程序退出,执行终止
if((pid=fork()) <0){
perror("process not create");
exit(EXIT_FAILURE);
}
//子进程号等于 0 表示子进程在执行,
else if(pid == 0){
//子进程负责从管道 1 的 0 端读,管道 2 的 1 端写,
//所以关掉管道 1 的 1 端和管道 2 的 0 端。
close(pipe1[1]);
close(pipe2[0]);
//每次循环从管道 1 的 0 端读一个整数放入变量 X 中,
//并对 X 加 1 后写入管道 2 的 1 端,直到 X 大于 10
do{
read(pipe1[0],&x,sizeof(int));
printf("child %d read: %d\n",getpid(),x++);
write(pipe2[1],&x,sizeof(int));
}while( x<=9 );
//读写完成后,关闭管道
close(pipe1[0]);
close(pipe2[1]);
//子进程执行结束
exit(EXIT_SUCCESS);
}
//子进程号大于 0 表示父进程在执行,
else{
//父进程负责从管道 2 的 0 端读,管道 1 的 1 端写,
//所以关掉管道 1 的 0 端和管道 2 的 1 端。
close(pipe1[0]);
close(pipe2[1]);
x=1;
//每次循环向管道 1 的 1 端写入变量 X 的值,并从
//管道 2 的 0 端读一整数写入 X 再对 X 加 1,直到 X 大于 10
do{
write(pipe1[1],&x,sizeof(int));
read(pipe2[0],&x,sizeof(int));
printf("parent %d read: %d\n",getpid(),x++);
}while(x<=9);
//读写完成后,关闭管道
close(pipe1[1]);
close(pipe2[0]);
}
}
程序运行结果: