首页 > 代码库 > Linux管道(匿名PIPE)

Linux管道(匿名PIPE)

管道基本概念

    管道是Unix中最古老的进程间通信的形式。

    我们把从一个进程连接到另一个进程的一个数据流称为一个“管道”

      如:ps aux | grep httpd | awk ‘{print $2}‘

 

管道示意图

 
 

管道的本质

    固定大小的内核缓冲区

管道限制

    1)管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道;

    2)匿名管道只能用于具有共同祖先的进程(如父进程与fork出的子进程)之间进行通信;[通常,一个管道由一个进程创建,然后该进程调用fork,此后父子进程共享该管道]

 

匿名管道pipe

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. SYNOPSIS  
  2.        #include <unistd.h>  
  3.        int pipe(int pipefd[2]);  

 

 

功能

    创建无名管道

参数

   Pipefd:文件描述符数组,其中pipefd[0]表示读端,pipefd[1]表示写端

 

管道创建示意图

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. //自己实现管道  
  2. void err_exit(string str);  
  3.   
  4. int main()  
  5. {  
  6.     int pipefd[2];  
  7.     if (pipe(pipefd) == -1)  
  8.         err_exit("pipe error");  
  9.   
  10.     pid_t pid;  
  11.     if ((pid = fork()) < 0)  
  12.         err_exit("fork error");  
  13.     if (pid == 0)   //In Child, Write pipe  
  14.     {  
  15.         close(pipefd[0]);  
  16.         //使得STDOUT_FILENO也指向pipefd[1],亦即ls命令的输出将打印到管道中  
  17.         dup2(pipefd[1],STDOUT_FILENO);  
  18.         //此时可以关闭管道写端  
  19.         close(pipefd[1]);  
  20.   
  21.         execlp("/bin/ls","/bin/ls",NULL);  
  22.   
  23.         //如果进程映像替换失败,则打印下面出错信息  
  24.         fprintf(stderr,"Child: execlp error");  
  25.         exit(0);  
  26.     }  
  27.   
  28.     //In Parent  
  29.     close(pipefd[1]);  
  30.     //使得STDIN_FILENO也指向pipefd[2],亦即wc命令将从管道中读取输入  
  31.     dup2(pipefd[0],STDIN_FILENO);  
  32.     //此时可以关闭管道读端  
  33.     close(pipefd[0]);  
  34.     execlp("/usr/bin/wc","/usr/bin/wc","-w",NULL);  
  35.   
  36.     //如果进程映像替换失败,则打印下面出错信息  
  37.     fprintf(stderr,"Parent: execlp error");  
  38.   
  39.     return 0;  
  40. }  
  41.   
  42. void err_exit(string str)  
  43. {  
  44.     perror(str.c_str());  
  45.     exit(EXIT_FAILURE);  
  46. }  

 

示例:管道编程实践

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. void err_exit(string str);  
  2.   
  3. int main()  
  4. {  
  5.     int pipefd[2];  
  6.     int ret;  
  7.     if ((ret = pipe(pipefd)) != 0)  
  8.     {  
  9.         err_exit("pipe error");  
  10.     }  
  11.   
  12.     pid_t pid = fork();  
  13.     if (pid == -1)  
  14.     {  
  15.         err_exit("fork error");  
  16.     }  
  17.   
  18.     if (pid == 0)   //In Child, Write pipe  
  19.     {  
  20.         close(pipefd[0]);   //Close Read pipe  
  21.         string str("I Can Write Pipe from Child!");  
  22.   
  23.         write(pipefd[1],str.c_str(),str.size());    //Write to pipe  
  24.         close(pipefd[1]);  
  25.         exit(0);  
  26.     }  
  27.   
  28.     //In Parent, Read pipe  
  29.     close(pipefd[1]);   //Close Write pipe  
  30.     char buf[1024];  
  31.     memset(buf,0,sizeof(buf));  
  32.     read(pipefd[0],buf,sizeof(buf));    //Read from pipe  
  33.     cout << "Read from pipe: " << buf << endl;  
  34.     close(pipefd[0]);  
  35.   
  36.     return 0;  
  37. }  
  38.   
  39. void err_exit(string str)  
  40. {  
  41.     perror(str.c_str());  
  42.     exit(EXIT_FAILURE);  
  43. }  

 

匿名管道读写规则

1)管道空时

    O_NONBLOCK disable:read调用阻塞,即进程暂停执行,一直等到有数据来到为止。

    O_NONBLOCK enable:read调用返回-1,errno值为EAGAIN。

 

2)管道满时

    O_NONBLOCK disable: write调用阻塞,直到有进程读走数据

    O_NONBLOCK enable:调用返回-1,errno值为EAGAIN

 

3)管道不停被写,写满

    O_NONBLOCK disable: write调用阻塞(Block)

    O_NONBLOCK enable:调用返回-1,errno值为EAGAIN

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. //示例:设置父进程Unblock读PIPE  
  2. int main()  
  3. {  
  4.     int pipefd[2];  
  5.     int ret;  
  6.     if ((ret = pipe(pipefd)) != 0)  
  7.     {  
  8.         err_exit("pipe error");  
  9.     }  
  10.   
  11.     pid_t pid = fork();  
  12.     if (pid == -1)  
  13.     {  
  14.         err_exit("fork error");  
  15.     }  
  16.   
  17.     if (pid == 0)   //In Child, Write pipe  
  18.     {  
  19.         sleep(10);  
  20.         close(pipefd[0]);   //Close Read pipe  
  21.         string str("I Can Write Pipe from Child!");  
  22.   
  23.         write(pipefd[1],str.c_str(),str.size());    //Write to pipe  
  24.         close(pipefd[1]);  
  25.         exit(0);  
  26.     }  
  27.   
  28.     //In Parent, Read pipe  
  29.     close(pipefd[1]);   //Close Write pipe  
  30.     char buf[1024];  
  31.     memset(buf,0,sizeof(buf));  
  32.   
  33.     //Set Read pipefd UnBlock!  
  34.     int flags = fcntl(pipefd[0],F_GETFL);  
  35.     flags |= O_NONBLOCK;  
  36.     ret = fcntl(pipefd[0],F_SETFL,flags);  
  37.     if (ret != 0)  
  38.     {  
  39.         err_exit("Set UnBlock error");  
  40.     }  
  41.   
  42.     int readCount = read(pipefd[0],buf,sizeof(buf));    //Read from pipe  
  43.     if (readCount < 0)  
  44.     {  
  45.         //read立刻返回,不再等待子进程发送数据  
  46.         err_exit("read error");  
  47.     }  
  48.     cout << "Read from pipe: " << buf << endl;  
  49.     close(pipefd[0]);  
  50.   
  51.     return 0;  
  52. }  

 

 

4)如果所有管道写端对应的文件描述符被关闭,则read返回0

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. int main()  
  2. {  
  3.     int pipefd[2];  
  4.     int ret;  
  5.     if ((ret = pipe(pipefd)) != 0)  
  6.     {  
  7.         err_exit("pipe error");  
  8.     }  
  9.   
  10.     pid_t pid = fork();  
  11.     if (pid == -1)  
  12.     {  
  13.         err_exit("fork error");  
  14.     }  
  15.   
  16.     if (pid == 0)   //In Child  
  17.     {  
  18.         //close all  
  19.         close(pipefd[0]);  
  20.         close(pipefd[1]);  
  21.         exit(0);  
  22.     }  
  23.   
  24.     //In Parent  
  25.     sleep(1);  
  26.     close(pipefd[1]);   //Close Write pipe, Now all pipefd[1] Closed!!!  
  27.     char buf[1024];  
  28.     memset(buf,0,sizeof(buf));  
  29.   
  30.     int readCount = read(pipefd[0],buf,sizeof(buf));    //Read from pipe  
  31.     if (readCount == 0)  
  32.     {  
  33.         cout << "OK, read 0 byte" << endl;  
  34.     }  
  35.     close(pipefd[0]);  
  36.   
  37.     return 0;  
  38. }  

 

 

5)如果所有管道读端对应的文件描述符被关闭,则write操作会产生信号SIGPIPE

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. void onSignalAction(int signalNumber)  
  2. {  
  3.     switch(signalNumber)  
  4.     {  
  5.     case SIGPIPE:  
  6.         cout << "receive signal SIGPIPE: " << signalNumber << endl;  
  7.         break;  
  8.     default:  
  9.         cout << "other signal" << endl;  
  10.         break;  
  11.     }  
  12. }  
  13.   
  14. int main()  
  15. {  
  16.     if (signal(SIGPIPE,onSignalAction) != 0)  
  17.     {  
  18.         err_exit("signal error");  
  19.     }  
  20.   
  21.     int pipefd[2];  
  22.     int ret;  
  23.     if ((ret = pipe(pipefd)) != 0)  
  24.     {  
  25.         err_exit("pipe error");  
  26.     }  
  27.   
  28.     pid_t pid = fork();  
  29.     if (pid == -1)  
  30.     {  
  31.         err_exit("fork error");  
  32.     }  
  33.   
  34.     if (pid == 0)   //In Child, Write pipe  
  35.     {  
  36.         //Wait Parent Close pipefd[0]  
  37.         sleep(1);  
  38.         close(pipefd[0]);  
  39.         string str("I Can Write Pipe from Child!");  
  40.   
  41.         write(pipefd[1],str.c_str(),str.size());    //Write to pipe  
  42.         close(pipefd[1]);  
  43.         exit(0);  
  44.     }  
  45.   
  46.     //In Parent, Close All Pipe  
  47.     close(pipefd[1]);  
  48.     close(pipefd[0]);  
  49.   
  50.     wait(NULL);  
  51.     return 0;  
  52. }  


Linux PIPE特征

    1)当要写入的数据量不大于PIPE_BUF时,Linux将保证写入的原子性。

    2)当要写入的数据量大于PIPE_BUF时,Linux将不再保证写入的原子性。

 

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. //示例:测试PIPE_BUF大小  
  2. int main()  
  3. {  
  4.     int pipefd[2];  
  5.     int ret = pipe(pipefd);  
  6.     if (ret < 0)  
  7.     {  
  8.         err_exit("pipe error");  
  9.     }  
  10.   
  11.     int flags = fcntl(pipefd[1],F_GETFL);  
  12.     flags |= O_NONBLOCK;  
  13.     ret = fcntl(pipefd[1],F_SETFL,flags);  
  14.     if (ret < 0)  
  15.     {  
  16.         err_exit("fcntl error");  
  17.     }  
  18.   
  19.     //Write test  
  20.     unsigned int countForTestPipe = 0;  
  21.     while (true)  
  22.     {  
  23.         ret = write(pipefd[1],"a",1);  
  24.         if (ret < 0)  
  25.         {  
  26.             break;  
  27.         }  
  28.         ++ countForTestPipe;  
  29.     }  
  30.   
  31.     cout << "size = " << countForTestPipe << endl;  
  32. }  
  33.   
  34. /**测试结果:Ubuntu 14.04 X64 
  35.   xiaofang@xiaofang-Lenovo-G470:~/apue/it$ ./main  
  36.   size = 65536 
  37. */  


 

附-管道容量查询

    man 7 pipe

 

 

 

附-深入理解文件描述符

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
    1. int main()  
    2. {  
    3.     close(STDIN_FILENO);  
    4.     if (open("readfile.txt",O_RDONLY) == -1)  
    5.     {  
    6.         err_exit("open read error");  
    7.     }  
    8.     close(STDOUT_FILENO);  
    9.     if (open("writefile.txt",O_WRONLY|O_TRUNC|O_CREAT,0644) == -1)  
    10.     {  
    11.         err_exit("open write error");  
    12.     }  
    13.   
    14.     if (execlp("/bin/cat","/bin/cat",NULL) == -1)  
    15.     {  
    16.         err_exit("execlp error");  
    17.     }  
    18.   
    19.     return 0;  

Linux管道(匿名PIPE)