beej导管示例说明

时间:2015-09-24 04:40:29

标签: c pipe ipc

以下代码是beej指南中给出的管道实现:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(void)
{
 int pfds[2];
 pipe(pfds);
 if (!fork()) {
 close(1); /* close normal stdout */
 dup(pfds[1]); /* make stdout same as pfds[1] */
 close(pfds[0]); /* we don't need this */
 execlp("ls", "ls", NULL);
 } else {
 close(0); /* close normal stdin */
 dup(pfds[0]); /* make stdin same as pfds[0] */
 close(pfds[1]); /* we don't need this */
 execlp("wc", "wc", "-l", NULL);
 }
 return 0;
}

我想问:

  1. close(0)之前执行dup(pfds[1])是否可能?如果是,那么在这种情况下,程序将不会按预期运行。
  2. 以下代码行的用途是什么:

    close(pfds[0]); /* we don't need this */
    close(pfds[1]); /* we don't need this */
    
  3. 如果不存在这些线条会有什么变化?

2 个答案:

答案 0 :(得分:1)

  

是否可能在dup(pfds [1])之前执行close(0)?如是,   那么在这种情况下,程序将不会按预期运行。

是的,在孩子拨打close(0)之前,父母可以成功完成dup(pfds[1])。但是,这不是问题。分叉新进程时,新进程将获取父进程内存地址空间的完整副本,包括打开的文件描述符(标有O_CLOEXEC标记的那些除外 - 请参阅fcntl(2))。因此,基本上每个进程都有自己的文件描述符的私有副本,并且是孤立的,可以随意使用该副本执行任何操作。

因此,当父调用close(0)时,它只关闭文件描述符0(stdin)的副本;它不会以任何方式影响孩子,它仍然引用stdin并且可以在需要时使用它(即使在这个例子中它也不会)。

  

以下代码行的用途是什么:

close(pfds[0]); /* we don't need this */
close(pfds[1]); /* we don't need this */

最佳做法要求您应关闭不使用的文件描述符 - close(pfds[0])就是这种情况。未使用的打开文件描述符占用了空间和资源,如果你不打算使用它,为什么要保持开放呢?

close(pfds[1])虽然更加微妙。只有当管道缓冲区中没有更多数据并且没有活动写入器时,管道才报告文件结束,即没有打开管道进行写入的实时进程。如果您没有在父级中关闭pfds[1],程序将永久挂起,因为wc(1)将永远不会看到输入的结束,因为有一个进程(wc(1)本身)具有管道开放写作,因此可以(但不会)写更多数据。

Tl; DR:close(pfds[0])只是一种好习惯,但不是强制性的; close(pfds[1])对于确保程序的正确性是绝对必要的。

答案 1 :(得分:-1)

问题1: 是的,完全有可能“关闭(0);” (在父级中)在“dup(pfds [1]);”之前执行(在孩子身上)。但由于这种情况发生在不同的过程中,孩子仍然会打开fd 0。

问题2: 关闭流程不会使用的管道末端是一种很好的簿记操作。这样,您可以在更复杂的程序中避免进一步的错误。在上面的场景中,子进程应该只读取管道。如果你关闭了孩子的写端,eny尝试写入它会导致错误,否则你可能会有一个难以检测的错误。