Linux进程间通信-管道之匿名管道 原创 Linux平台 2022年2月11日 18:56 夏至未至 1969 当前内容 3331 字,在路上,马上到,马上到 ### 目录 [TOC] ### 匿名管道原理 一个进程先创建一个管道,然后fork出一个子进程,父进程子进程的进程级信息是独立的,系统级信息是共享的,然后各自关闭一端,取决于谁读谁写,然后各自根据返回的文件描述符,最后对一页内存进行读写,达到通信的目的。没有名字的匿名管道是一种最基本的IPC机制。 ### 匿名管道实现 #### 函数原型 #include int pipe(int pipefd[2]); 入参:一个整型数组 pipefd[0]表示管道的读端, pipefd[1]表示管道的写端. 返回值:调用成功返回0,调用失败返回-1,同时errno会被设置 #### 函数意图 管道可以由pipe函数创建,调用pipe函数时在内核中开辟一块缓冲区(称为管道)用于通信,它有一个读端一个写端,然后通 过pipefd参数传出给用户程序两个文件描述符。 pipefd[0] pipefd[1] (很好记,就像0是标准输入1是标准输出一样)。所以管道在用户程序看起来就像一个打开 的文件,通过read(pipe[0]),或者write(pipe[1]), 向这个文件读写数据,其实是在读写内核缓冲区。 ### 匿名管道扩展 #### 通信原理图 父进程创建管道,父进程fork出子进程,现在就有两个有血缘关系的父子进程,下面要说的就是两进程间的通信: ![](http://www.codecomeon.com:80/group1/M00/00/00/rBlbH2IGPyWAG17pAAG1BNaZsGA7310041) 存在于两个进程间的管道,两边都有读和写的功能,`匿名管道是单向的`,能写就不能读,能读就不能写,所以,父进程需要关闭了读端,也就意味着,父亲要写数据到管道;而子进程关闭了写端,也就意味着,子进程只能读管道间的数据,这样就完成了一次进程间的通信。 ![](http://www.codecomeon.com:80/group1/M00/00/00/rBlbH2IGP3SAQRRTAAEMcmNY5Yk8917433) #### 原理图解 1. 父进程调用pipe开辟管道,得到两个文件描述符指向管道的两端。 2. 父进程调用fork创建子进程,那么子进程也有两个文件描述符指向同一管道。 3. 父进程关闭管道读端,子进程关闭管道写端。父进程可以往管道里写,子进程可以从管道里读,管道是用环形队列实现的,数据从写端流入从读端流出,这样就实现了进程间通信。 #### 实操代码 #include #include #include #include int main() { int _pipe[2]; int ret = pipe(_pipe); //类似两个文件指针,指向两个流,0是读,1是写 if(ret == -1) { printf("创建管道失败,错误码是:%d,%d\n",error,strerror(error)); return 1; } pid_t id = fork(); //创建子进程 if(id < 0) { printf("子进程错误\n"); return 2; } else if(id == 0) //child 子进程相关操作 子进程写 { close(_pipe[0]); //子进程关闭了读 自然就选择了写 int i = 0; char* mesg = NULL; while(i < 20) { mesg = "我是子进程,我在写,我会被父进程读出来......"; write(_pipe[1],mesg,strlen(mesg)); sleep(2); //每写一次延时2秒 i++; } //写完之后关闭写端,但是读端继续读 ////////////////////////////// close(_pipe[1]); ////////////////////////////// } else //父进程相关操作 父进程读 { close(_pipe[1]); //关闭了写,也就只能读了 char _mesg[1024]; int j = 0; int i = 0; while(j < 200) { memset(_mesg,'\0',sizeof(_mesg)); int ret = read(_pipe[0],_mesg,sizeof(_mesg)); printf("%s,%d,%d\n",_mesg,ret,i++); j++; } //waitpid //////////////////////////// if(waitpid(id,NULL,0)<0) { return 3; } /////////////////////////// } return 0; } ### 匿名管道注意点 #### 匿名管道限制 1. 两个进程通过一个管道只能实现单向通信。比如上面的例子,父进程写子进程读,如果有时候也需要子进程写父进程读,就必须另开一个管道。 2. 管道的读写端通过打开的文件描述符来传递,因此要通信的两个进程必须从它们的公共祖先那里继承管道文件描述符。上面的例子是父进程把文件描述符传给子进程之后父子进程之间通信,也可以父进程fork两次,把文件描述符传给两个子进程,然后两个子进程之间通信, 总之需要通过fork传递文件描述符使两个进程都能访问同一管道,它们才能通信。 也就是说,管道通信是需要进程之间有关系。 #### 匿名管道特殊情况 使用管道需要注意以下4种特殊情况(假设都是阻塞I/O操作,没有设置O_NONBLOCK标志): 1. 如果所有指向管道写端的文件描述符都关闭了(管道写端的引用计数等于0),而仍然有进程从管道的读端读数据,那么管道中剩余的数据都被读取后,再次read会返回0,就像读到文件末尾一样。 2. 如果有指向管道写端的文件描述符没关闭(管道写端的引用计数大于0),而持有管道写端的进程也没有向管道中写数据,这时有进程从管道读端读数据,那么管道中剩余的数据都被读取后,再次read会阻塞,直到管道中有数据可读了才读取数据并返回。 3. 如果所有指向管道读端的文件描述符都关闭了(管道读端的引用计数等于0),这时有进程向管道的写端write,那么该进程会收到信号SIGPIPE,通常会导致进程异常终止. 4. 如果有指向管道读端的文件描述符没关闭(管道读端的引用计数大于0),而持有管道读端的 进程也没有从管道中读数据,这时有进程向管道写端写数据,那么在管道被写满时再次write会阻塞,直到管道中有空位置了才写入数据并返回。 本文标题: Linux进程间通信-管道之匿名管道 本文作者: 夏至未至 发布时间: 2022年2月11日 18:56 最近更新: 2022年2月11日 19:17 原文链接: 许可协议: 署名-非商业性-禁止演绎 4.0 国际(CC BY-NC-ND 4.0) 请按协议转载并保留原文链接及作者 进程间通信(8) 匿名管道(1) 上一个 Linux进程间通信-管道之命名管道 下一个 Linux下进程间通信-六种机制 当前文章评论暂未开放,请移步至留言处留言。