在execlp()之后关闭管道是否必要?

在陈述我的问题之前,我已经阅读了几个关于堆栈溢出的相关问题,例如UNIX中的管道和复制函数 ,以及其他几个,但没有澄清我的困惑。

首先,代码,这是“Beginning Linux Programming”,第4版,第13章的示例代码:

#include  #include  #include  #include  int main() { int data_processed; int file_pipes[2]; const char some_data[] = "123"; pid_t fork_result; if (pipe(file_pipes) == 0) { fork_result = fork(); if (fork_result == (pid_t)-1) { fprintf(stderr, "Fork failure"); exit(EXIT_FAILURE); } if (fork_result == (pid_t)0) // Child process { close(0); dup(file_pipes[0]); close(file_pipes[0]); // LINE A close(file_pipes[1]); // LINE B execlp("od", "od", "-c", (char *)0); exit(EXIT_FAILURE); } else // parent process { close(file_pipes[0]); // LINE C data_processed = write(file_pipes[1], some_data, strlen(some_data)); close(file_pipes[1]); // LINE D printf("%d - wrote %d bytes\n", (int)getpid(), data_processed); } } exit(EXIT_SUCCESS); } 

执行结果是:

momo @ xue5:〜/ TestCode / IPC_Pipe $ ./a.out

10187 – 写了3个字节

momo @ xue5:〜/ TestCode / IPC_Pipe $ 0000000 1 2 3

0000003

MOMO @ xue5:〜/ TestCode / IPC_Pipe $

如果您注释了LINE A,LINE C和LINE D ,结果与上面相同。 我理解结果,子进程通过自己的stdin连接到管道从父进程获取数据,并将’od -c’结果发送到它的stdout。

但是,如果您评论LINE B ,结果将是:

momo @ xue5:〜/ TestCode / IPC_Pipe $ ./a.out

10436 – 写了3个字节

MOMO @ xue5:〜/ TestCode / IPC_Pipe $

没有’od -c’的结果! ‘od -c’是由execlp()启动而不是执行,或者它的输出没有指向stdout? 一种可能性是’od’的read()被阻止,因为如果你注释LINE B,则子文件的写文件描述符file_pipes [1]是打开的。但是注释LINE D,它让父文件的文件描述符file_pipes [1]写入,仍然可以有’od -c’输出。

而且,为什么我们需要在execlp()之前关闭管道? execlp()将使用’od’中的新图像替换进程映像,包括stack,.data,.heap,.text。 这是否意味着,即使你没有将子文件中的file_pipes [0]和file_pipes [1]关闭为LINE A和B,file_pipes [0]和file_pipes [1]仍将被execlp()“销毁”? 从代码的结果来看,它不是。 但我错在哪里?

非常感谢您在这里的时间和努力~~

在execlp()之后关闭管道是否必要?

它不是绝对必要的,因为它取决于管道的使用方式。 但总的来说,如果过程不需要pipe端,则应该关闭它。

为什么我们需要在execlp()之前关闭管道? execlp()将替换过程映像

因为文件描述符(默认情况下)在exec调用中保持打开状态。 从手册页 :“默认情况下,文件描述符在execve()上保持打开状态。标记为close-on-exec的文件描述符已关闭;请参阅fcntl(2)中FD_CLOEXEC的说明。”

但是,如果您评论LINE B,……没有’od -c’结果!

这是因为od进程从stdin读取直到它获得EOF 。 如果进程本身没有关闭file_pipes[1]那么它将不会看到EOF因为管道的写入端不会被打开它的所有进程完全关闭。

如果您注释了LINE A,LINE C和LINE D,他的结果与上面相同

这是因为A和C处的文件描述符是管道的读取端,并且不会阻止任何人等待它被关闭(如上所述)。 D处的文件描述符是写入结束而不关闭它确实会导致问题。 但是,即使代码没有在该文件描述符上显式调用close ,它仍将被关闭,因为该进程退出。

而且,为什么我们需要在execlp()之前关闭管道? execlp()将使用’od’中的新图像替换进程映像,包括stack,.data,.heap,.text。

是的,exec-family函数(包括execlp()用指定程序的副本替换调用进程的进程映像。 但是进程的打开文件描述符表不是进程映像的一部分 – 它由内核维护,并且它在exec中存活。

这是否意味着,即使你没有将子文件中的file_pipes [0]和file_pipes [1]关闭为LINE A和B,file_pipes [0]和file_pipes [1]仍将被execlp()“销毁”?

变量 file_pipesexeclp()破坏,但这只是程序文件描述符的内部存储。 描述符只是内核为进程维护的表的整数索引。 丢失文件描述符值的跟踪不会导致关联文件。 事实上,这是资源泄漏的一种forms。

从代码的结果来看,它不是。 但我错在哪里?

如上所述。

此外,当进程退出时,其所有打开的文件描述符都将关闭,但文件描述符所引用的内核中的基础打开文件描述符仅在没有引用它的打开文件描述符时才会关闭。 其他进程可以保留其他打开文件描述符,这是通过fork()inheritance它们的结果。

现在关于在执行od之前子进程没有关闭file_pipes[1]时会发生什么的具体问题,你可以通过ps命令检查进程列表来获得线索。 您将看到子od进程仍在运行(如果您已多次测试,可能会有几个)。 为什么?

那么, od怎么知道什么时候退出? 它处理整个输入,因此它必须在到达输入结束时退出。 但是管道输入的结束并不意味着现在没有更多的数据可用,因为稍后可能会有更多数据被写入管道的写入端。 当写入结束时,管道上的输入结束。 如果孩子在执行前没有关闭file_pipes[1] ,那么它可能会无限期地保持打开状态,因为在执行后,孩子不再知道它拥有它。