fork()后,父子进程将有各自独立的存储空间,他们之间是没法像同一进程之间经过传参或使用全局变量来进行数据交换的。数组
怎么办?管道就是一种简单的实现进程间通讯的方式。spa
管道被建立后,将获得两个文件描述符file_des[1]和file_des[0](描述符的名字取决于管道建立的参数)。code
file_des[1]用于向管道中写入,file_des[0]用于从管道中读。读的顺序和写的顺序一致。blog
这两个文件描述符可被子进程继承。因此,父子进程只要操做各自的这两个文件描述符就能够实现彼此通讯。继承
一样,一个进程建立了管道后,全部继承于它的子进程、孙子进程之间均可以经过这个管道来进行通讯。进程
管道的建立方式:ip
经过系统调用pipe,头文件unistd.h。string
int pipe (int file_des[2]); 建立失败返回-1,成功返回0,并获得两个文件描述符file_des[1]和file_des[0]。it
#include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> int main(){ int file[2]; //文件描述符数组 char buffer[BUFSIZ+1] const char data="hello"; int res = pipe(file); if(res == 0) //pipe调用成功,管道建立 { printf("pipe created!\n"); if(fork()==0){ read(file[0],buffer,BUFSIZ); //子进程从管道中读 printf("read %s\n",buffer); exit(EXIT_SUCCESS); }else{ write(file[1],data,strlen(date)); //父进程向管道中写 printf("write %s\n",data); } exit(EXIT_SUCCESS); }
那么,将会获得看到输出:pip
write hello
read hello
也有可能上下两行对调(与进程调度有关)。上述例子中fork()后,if 和 else里的内容也可对调,变成父进程读,子进程写。
该图清晰地说明了不一样进程经过管道交换数据的方式。注意,父子进程分别有各自用于读写管道的文件描述符。
对于管道的write操做,若管道已满,write会被阻塞,直到另外一头有read将管道中数据取走;若全部read端都关闭了,write将返回-1,并设置error为EPIPE。
对于管道的read操做:
1.若管道为空,read将阻塞,直到另外一头有write向管道写;
2.若管道不为空,若read调用计划读取的字节数a > 管道中的字节数b ,那么就读取b个字节,返回b;若a < b,则读取a个字节,返回a。
3.管道的写端已关闭(必须是引用这个管道的全部进程的file_des[1]均关闭),read将返回0。
管道应注意的问题:
当两个进程同一时间段要实现双向交换数据时,应该使用两个管道,不然一个进程对同一个管道既读又写,很容易读到本身写进去的内容。
两个进程同一时间段使用两个管道进行双向数据交换时,安排不当有可能产生死锁问题:
例:父进程向管道A写10个数据,子进程从A读1个数据,读到的数据经处理写入管道B,父进程从B得到处理结果。
此时,若父进程向A写入的某个数据过大,管道已满,父进程write将阻塞,此时,子进程向B写入的数据也很大,管道B也满了,子进程write也阻塞,这时会陷入死锁。