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

时间:2016-06-02 03:19:19

标签: c pipe dup

在陈述我的问题之前,我已经阅读了几个关于堆栈溢出的相关问题,例如pipe & dup functions in UNIX和其他几个,但没有澄清我的困惑。

首先,代码是一个示例代码,来自“Linux初代编程”,第4版,第13章:

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

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连接到管道从父母那里获取数据,然后发送&#39; od -c&#39;结果是它的标准。

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

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

10436 - 写了3个字节

momo @ xue5:〜/ TestCode / IPC_Pipe $

否&#39; od -c&#39;结果<!/强> 是&#39; od -c&#39;由execlp()启动而不是执行,或者它的输出没有指向stdout?一种可能性是读取()&#39; od&#39;被阻止,因为如果您注释LINE B,则子文件的写文件描述符file_pipes [1]是打开的。但是注释LINE D,它让父文件的写文件描述符file_pipes [1]打开,仍然可以使用&#39; od - C&#39;输出

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

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

2 个答案:

答案 0 :(得分:3)

  

在execlp()后面是否需要关闭管道?

这不是必需的,因为它取决于管道的使用方式。但总的来说,如果流程不需要cookie:'time=s%3A1464743488946.WvSJxbCspOG3aiGi4zCMMR9yBdvS%2B6Ob2f3OG6%2FYCJM'结束,则应该关闭它。

  

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

因为文件描述符(默认情况下)在pipe次调用之间保持打开状态。从man page:“默认情况下,文件描述符在execve()中保持打开。标记为close-on-exec的文件描述符将被关闭;请参阅fcntl(2)中FD_CLOEXEC的描述。”

  

但是,如果您评论了LINE B,......没有'od -c'结果!

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

  

如果你评论了LINE A,LINE C和LINE D,他的结果与上面相同

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

答案 1 :(得分:2)

  

而且,为什么我们需要在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()破坏,但这只是程序文件描述符的内部存储。描述符只是内核为进程维护的表的整数索引。 丢失文件描述符值的跟踪不会导致关联的文件被关闭。事实上,这是资源泄漏的一种形式。

  

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

如上所述。

此外,当进程退出时,其所有打开的文件描述符都将关闭,但文件描述符所引用的内核中的基础打开文件描述 ion 仅在没有打开文件时关闭引用它的描述符仍然存在。其他进程可能会保留其他打开文件描述符,因为它们是通过fork()继承的。

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

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