前言每个进程都有不同的用户地址空间,任何一个进程的全局变量在另一个进程中是看不到的,所以进程之间的数据交换必须经过内核。在内核中打开一个缓冲区。进程1从用户空间拷贝数据到内核缓冲区,进程2从内核缓冲区读取数据。内核提供的这种机制称为进程间通信(IPC,InterProcessCommunication)。如下所示。什么是管道如果你用过Linux命令,那么我们对这个名词并不陌生。我们经常通过“|”来使用管道,比如ls-l|grepstring。管道是将数据流从一个进程连接到另一个进程的通道,通常一个进程的输出通过管道传输到另一个进程的输入。高级管道语法#includeFILE*popen(constchar*command,constchar*open_mode);intpclose(FILE*stream_to_close);使用popen会启动另一个新进程,然后向它传递数据或从它接收数据。command是要运行的程序名和对应的参数,open_mode只能是"r"或"w"popen返回一个FILE类型的指针,然后就可以使用I/O文件函数对其进行操作了。当open_mode为“r”时,表示主程序可以从被调用程序中读取数据。当open_mode为“w”时,表示主程序可以向被调用程序写入数据。pclose用于关闭由popen创建的关联文件流。pclose仅??在popen启动的进程结束后返回。如果在调用pclose时被调用进程仍在运行,pclose将等待它结束。它返回包含已关闭文件流的进程的退出代码。示例代码////创建者:HarrisZhu//文件名:test.c//作者:HarrisZhu//创建于:2017-08-2001:18//最后修改://更新计数:2017-08-2001:18//标签://描述://结论:////=========================================================================#include#include#include#include#defineERR_EXIT(m)\do{\perror(m);\退出(退出失败);\}while(0)intmain(intargc,char**argv){FILE*rdFP=NULL;文件*writeFP=NULL;字符buf[BUFSIZ+1];intnum_of_chars=0;memset(buf,'\0',sizeof(buf));//打开ls作为读取接口rdFP=popen("ls-l","r");if(!rdFP){ERR_EXIT("无法打开读取管道");}//打开grep作为写入接口wrFP=popen("grep\\\\-rw-rw-r--","w");if(!wrFP){ERR_EXIT("无法打开写入管道");}如果(路FP&&wrFP){//从ls中读取BUFSIZ个字符num_of_chars=fread(buf,sizeof(char),BUFSIZ,rdFP);while(num_of_chars>0){buf[num_of_chars]='\0';//把数据写入grepfwrite(buf,sizeof(char),num_of_chars,wrFP);//循环读取数据,直到读取完所有数据num_of_chars=fread(buf,sizeof(char),BUFSIZ,rdFP);}//关闭文件流pclose(rdFP);pclose(wrFP);}退出(EXIT_SUCCESS);}输出-rw-rw-r--。1harriszhharriszh126八月2001:28makefile-rw-rw-r--。1harriszhharriszh1560Aug2001:32test.c因为grep,我们需要用4个字符转义-在例子中,否则grep会把它作为一个选项另一种写法是wrFP=popen("grep\'\\-rw-rw-r--\'","w");优点和缺点popen运行程序时,先启动shell,然后当命令字符串作为参数传递给它时,优点是参数扩展由shell完成,所以可以使用各种缺点wildcards就是每次打开一个程序都需要启动一个shell,也就是说一个popen调用启动了两个进程。从效率和资源的角度来看,popen并不理想。底层管道popen是高层函数,pipe是底层调用。它不需要启动shell来解释命令,它可以提供对数据的更多控制语法#includeintpipe(intfiledes[2]);用法当管道函数被调用时,在内核中打开一个缓冲区(称为管道)用于通信。它有读端和写端,然后通过filedes参数向用户程序发送两个文件描述符,filedes[0]指向管道的读端,filedes[1]指向管道的写端pipeline(好记,就像0是标准输入,1是标准输出)。所以管道在用户程序中看起来就像是一个打开的文件,通过read(filedes[0]);向这个文件读写数据。或写(文件[1]);实际上就是读写内核缓冲区。如果调用成功,管道函数返回0,如果调用失败,则返回-1。通信机制造示例代码////创建者:HarrisZhu//文件名:test.c//作者:HarrisZhu//创建于:2017-08-1913:28//最后修改://更新计数:2017-08-1913:28//标签://描述://结论:////=======================================================================#include#include#include#include#include#include#include#include#include#include#include#defineERR_EXIT(m)\do{\perror(m);\退出(退出失败);\}while(0)intmain(intargc,char**argv){intpipefd[2];if(pipe(pipefd)==-1){ERR_EXIT("打开管道错误");}pid_tpid;pid=fork();#ifdefSWITCHcharr_buf[100]={0};charw_buf[100]="你好\0";开关(pid){case-1:ERR_EXIT("分叉错误");休息;情况0:关闭(pipefd[0]);printf("儿子:发送%s,size=%d\n",w_buf,sizeof(w_buf));写(pipefd[1],w_buf,sizeof(w_buf));关闭(管道[1]);退出(退出成功);休息;默认值:关闭(pipefd[1]);读(pipefd[0],r_buf,100);printf("paraent:接收%s,size=%d\n",r_buf,sizeof(r_buf));关闭(pipefd[0]);//退出(EXIT_SUCCESS);休息;}#elseif(pid<0){ERR_EXIT("分叉错误");}else{if(pid==0){close(pipefd[0]);char*w_buf="你好";printf("son:发送%s,size=%d\n",w_buf,strlen(w_buf));写(pipefd[1],“你好”,5);关闭(管道[1]);退出(退出成功);}else{charr_buf[100]={0};关闭(管道[1]);读(pipefd[0],r_buf,5);printf("paraent:接收%s,size=%d\n",r_buf,strlen(r_buf));关闭(管道[0]);退出(退出成功);}}#endifreturn0;}说明父进程调用pipe打开一个管道,得到两个指向管道两端的文件描述符。父进程调用fork创建子进程,那么子进程也有两个文件描述符指向同一个管道。父进程关闭管道的写端,子进程关闭管道的读端。子进程可以写入管道,父进程可以从管道读取。管道是用循环队列实现的,数据从写端流入,从读端流出,从而实现进程间通信。注意,两个进程只能通过管道实现单向通信。比如上面的例子,父进程读了子进程的写。如果有时候子进程需要读父进程的写,就必须再开一个管道。管道的读写端通过打开的文件描述符传递,因此要通信的两个进程必须从它们共同的祖先那里继承管道文件描述符。上面的例子是父进程将文件描述符传递给子进程后,父进程与子进程之间的通信。也可以fork两次父进程,将文件描述符传递给两个子进程,然后两个子进程之间进行通信。简而言之,您需要通过fork传递文件描述符,以便两个进程都可以访问同一个管道,以便它们可以进行通信。通信过程需要是父子进程关系,这极大地限制了它的应用。这时候我们可以使用命名管道来解决问题。pipe的使用和原理都很简单,本文不做过多讲解。有问题请写Email参考:Linux系统编程流水线