使用Linux pipe的过程编程模型()

从http://pubs.opengroup.org/onlinepubs/009604599/functions/pipe.html :

pipe()函数将创build一个pipe道,并将两个文件描述符分别放入参数fildes [0]和fildes [1]中,这些参数指向pipe道的读写端的打开文件描述。

有一个父母将数据写入其子的例子:

int fildes[2]; const int BSIZE = 100; char buf[BSIZE]; ssize_t nbytes; int status; status = pipe(fildes); if (status == -1 ) { /* an error occurred */ ... } switch (fork()) { case -1: /* Handle error */ break; case 0: /* Child - reads from pipe */ close(fildes[1]); /* Write end is unused */ nbytes = read(fildes[0], buf, BSIZE); /* Get data from pipe */ /* At this point, a further read would see end of file ... */ close(fildes[0]); /* Finished with pipe */ exit(EXIT_SUCCESS); default: /* Parent - writes to pipe */ close(fildes[0]); /* Read end is unused */ write(fildes[1], "Hello world\n", 12); /* Write data on pipe */ close(fildes[1]); /* Child will see EOF */ exit(EXIT_SUCCESS); } 
  1. 我想知道如何使用pipe道与其子的沟通的父母可以确保它不会发生,父母运行之前,运行read()并且孩子在父母完成写之前完成读取?

  2. 我想知道如果在父母创build一个pipe道可以用于父母和孩子之间的双向沟通​​,或者只是从父母到孩子的一种方式而不是另一种方式?

    如果孩子可以通过pipe道将数据发送给父母,程序将是什么样子?

  3. 是一个pipe道,像一个两端的真正的pipe道。 fildes [0]和fildes [1]分别代表两端吗?

    如果一个pipe道是双向的,那么对于一个读(写),父或者孩子来说读端和写端的含义是什么?

感谢致敬!

  1. 除非指定O_NONBLOCK ,否则read将会阻塞并等待数据可用。 所以没关系; 如果数据不可用,它将等待它。
  2. 不,管道不是双工的。 如果你想双向通信,你应该做两个管道(这会给你四个文件描述符总数)。 fildes[0]始终是读取 (输出)结束, fildes[1]写入 (输入)结束。 所以你完全按照你所做的来设置一个管道(父母得到书写结束,孩子读取结束,所以父母可以发送给孩子),并以相反的方式设置另一个管道(你让父母保持阅读的结束,并给孩子写作结束)。
  3. fildes[0]fildes[1]的确代表管道的端部,但它们是不可互换的。 一个用于将数据“放入”管道,另一个用于从管道中取出数据。

如果您需要更多的澄清,请随时询问; 我意识到这可能会让新手感到困惑。

更新回答问题的评论:

  1. 任何进程都可以调用pipe() ,其子进程将继承它接收的文件描述符(除非关闭它们)。 也就是说,他们通过多次fork()调用生存。 所以你可以编写一个程序,将文件描述符给孙辈,曾孙等等,如果你想的话。
  2. 当然,两个兄弟进程可以这样沟通。 父进程调用pipe()创建一对(或者如果你想双向通信的话需要两对),然后分叉两次。 然后你可以有一个孩子使用fildes[0] ,另一个孩子使用fildes[1] 。 父母应该既不使用文件描述符。 瞧! 儿童与共同父母之间的沟通。 这个简单的(尽管可能是不直观的)功能是多么的强大。 这看起来像下面的例子。
  3. 图片fork()作为一个克隆机器。 在进入克隆机之前,用pipe()来制作一对使用特殊保密频率的对讲机。 一个只能传输; 对方只能接收。 当你使用克隆机器时,出现两个副本(原始的,即父母和克隆,即孩子)。 每个副本现在携带一对相同的对讲机。 如果原始设备破坏他的“接收”设备,克隆破坏他的“传输”设备,则原始设备可以使用对讲机与克隆进行通话(但不能反过来)。

    但是你看,这里的关键思想不是pipe()连接两个进程(虽然这是它的主要用途),而是fork()期间重复的东西。 您可以多次重新进入克隆机器并获得许多对讲机,因此定义“谁在跟谁说话”(哪个进程通过管道连接)并不是克隆机器功能的特定方面,对讲机。

    而是由谁来决定实际使用哪个端点,这完全在你的掌握之中。 如果你想的话,你可以让孩子关闭两个端点(摧毁两个对讲机),这会让父母自言自语(这很愚蠢,可能没有用处,但这是可能的 )。 我意识到这是一个切线,但我试图说明这个基本思想是如何工作的,这样你就可以理解管道可能发生的事情。

与管道交谈的同级进程示例(单向):

 int fildes[2]; int status; status = pipe(fildes); if (status == -1) { ... } // first fork switch (fork()) { case -1: // handle error break; case 0: // child 1 (with the writing end -- close the writing end) close(fildes[0]); // your logic for child 1 here, using fildes[1] to write return; default: // not child 1: close the writing end close(fildes[1]); break; } // second fork switch (fork()) { case -1: // handle error break; case 0: // child 2 (with the reading end) // your logic for child 2 here, using fildes[0] to read return; } // still in the parent // logic for the common parent here, after forking both children 
  1. 如果孩子运行了read() ,只要没有阻塞被设置为描述符,它就会阻塞直到数据可用。 如果孩子完成阅读并关闭管道,父母将得到一个SIGPIPE信号和write()将中止。

  2. 管道通常是单向通信。 有(流言),双向管道的实现,但我从来没有遇到过其中之一。

  3. 如2所述,管道最初被定义为单向的,所以父母有结束的地方写信给孩子,结束的地方从哪里读取。 如果双向管道可用,我这个孩子可以写入到最后,而父节点从最后读取。

也许man 7 pipe也会帮助你。