进程通信的实现--管道

语法


  #include <unistd.h>

       int pipe(int pipefd[2]);

       #define _GNU_SOURCE             /* See feature_test_macros(7) */
       #include <fcntl.h>              /* Obtain O_* constant definitions */
       #include <unistd.h>

       int pipe2(int pipefd[2], int flags);

描述

pipe() 创建一个单向的可用作进程间通信的数据通道。pipefd数组用作返回管道两端的文件描述符。pipefd[0]代表管道的读取端。pipefd[1]用作写入端。数据被写入到由内核所缓冲的管道写入端。

如果 flags 为 0,那么pipe2()就和pipe()一样。下面这些变量可以在flags中按位或运算来获得不同的功能:

O_CLOEXEC

为两个文件描述符设置close-on-exec (FD_CLOEXEC)标志。在 open 中查看相同标志的描述,了解它为什么非常有用。

O_DIRECT( 自从 Linux 3.4 )

创建一个在 packet 模式下执行 I/O 的管道。每次对管道的 write 都被当作分开的包处理,并且每次从管道中 read 都会依次读取一个包。注意以下几点:

  • 写入超过 PIPE_BUF 字节(查看 pipe(7))将会分为多个包。常数 PIPE_BUF定义在 <limits.h>中。
  • 如果 read 确定的缓冲区大小比下一个包小,这时,包中需要的字节被读取,超出的字节被丢弃。指定缓冲区大小为 PIPE_BUF 就足以读取尽可能大的包。
  • 不支持 0 长度的包。(read 指定缓冲区大小为 0 是一个空操作,返回 0。)
    老版本内核不支持这个标志将会通过 EINVAL 错误指出。
    自从 Linux 4.5,可以通过 fcntl 改变管道文件描述符的 O_DIRECT 设置。

O_NONBLOCK

为两个新开的文件描述符设置 O_NONBLOCK 文件状态标志。使用这个标志节省额外的 fcntl 调用以达到同样的效果。

返回值

成功,返回 0。错误,返回 -1 并且设置 errno。
在 Linux 上(和其他的系统),pipe()不在失败时修改pipefd。在 POSIX.1-2016 中添加了规范这一行为的要求。Linux 规定的 pipe2() 系统调用同样在失败时不修改 pipefd

错误

  • EFAULT 无效 pipefd
  • EINVAL(pipe2())flags 值非法
  • EMFILE 达到之前进程所限制的打开文件描述符的数目
  • ENFILE 达到系统所限制的打开文件描述符的数目
  • ENFILE 管道空间达到用户强行限制的内存,或者调用者没有权限。查看pipe(7)

版本

pipe2()在 Linux 2.6.27 中被添加进去;glibc 支持从 2.9 版本后开始。

标准

pipe(): POSIX.1-2001,POSIX.1-2008。
pipe2() 是 Linux 特有。

范例

下面的程序创建一个管道,然后 frok 去创建一个子进程。子进程继承了一套指向同一管道的文件描述符。fork 之后,每个进程关闭了管道不需要的文件描述符。父进程这时写入管道从命令行参数获取的字符串,然后子进程依次按字节读取这个字符串并显示在标准输出上。


      #include <sys/types.h>
       #include <sys/wait.h>
       #include <stdio.h>
       #include <stdlib.h>
       #include <unistd.h>
       #include <string.h>

       int
       main(int argc, char *argv[])
       {
           int pipefd[2];
           pid_t cpid;
           char buf;

           if (argc != 2) {
               fprintf(stderr, "Usage: %s <string>\n", argv[0]);
               exit(EXIT_FAILURE);
           }

           if (pipe(pipefd) == -1) {
               perror("pipe");
               exit(EXIT_FAILURE);
           }

           cpid = fork();
           if (cpid == -1) {
               perror("fork");
               exit(EXIT_FAILURE);
           }

           if (cpid == 0) {    /* Child reads from pipe */
               close(pipefd[1]);          /* Close unused write end */

               while (read(pipefd[0], &buf, 1) > 0)
                   write(STDOUT_FILENO, &buf, 1);

               write(STDOUT_FILENO, "\n", 1);
               close(pipefd[0]);
               _exit(EXIT_SUCCESS);

           } else {            /* Parent writes argv[1] to pipe */
               close(pipefd[0]);          /* Close unused read end */
               write(pipefd[1], argv[1], strlen(argv[1]));
               close(pipefd[1]);          /* Reader will see EOF */
               wait(NULL);                /* Wait for child */
               exit(EXIT_SUCCESS);
           }
       }

via: http://man7.org/linux/man-pages/man2/pipe.2.html

译者:LuuMing

感谢稀稀拉拉的赞赏