【问题标题】:Is closing a pipe necessary when followed by execlp()?紧跟 execlp() 时是否需要关闭管道?
【发布时间】:2016-06-02 03:19:19
【问题描述】:

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

首先是代码,这是来自“Beginning Linux Programming”第 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,结果同上。 我理解结果,孩子通过连接到管道的自己的标准输入从其父母那里获取数据,并将“od -c”结果发送到它的标准输出。

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

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

10436 - 写入 3 个字节

momo@xue5:~/TestCode/IPC_Pipe$

没有“od -c”结果! 由 execlp() 启动的“od -c”是否未执行,或者其输出未定向到标准输出?一种可能性是 'od' 的 read() 被阻止,因为如果您评论了 LINE B,则子级的写入文件描述符 file_pipes[1] 是打开的。但是评论 LINE D,这会让父级的写入文件描述符 file_pipes[1] 打开, 仍然可以有 'od -c' 输出。

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

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

【问题讨论】:

    标签: c pipe dup


    【解决方案1】:

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

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

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

    因为文件描述符(默认情况下)在 exec 调用中保持打开状态。来自man page:“默认情况下,文件描述符在 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,它仍然会因为进程退出而关闭。

    【讨论】:

    • " 但是,即使代码没有显式地对该文件描述符调用 close,它仍然会因为进程退出而关闭。"就是这样!我忘记了进程退出时 fd 将被关闭。谢谢~~
    【解决方案2】:

    还有,为什么我们需要在 execlp() 之前关闭管道? execlp() 将使用来自“od”的新图像替换进程图像,包括堆栈、.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() 继承它们,其他进程可能会持有其他打开的文件描述符。

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

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

    【讨论】:

    • 对文件描述符行为的清晰解释。非常感谢,我的困惑解开了~~
    猜你喜欢
    • 1970-01-01
    • 2018-07-15
    • 2012-08-03
    • 2016-11-20
    • 1970-01-01
    • 2019-02-15
    • 2021-03-10
    • 2010-11-26
    • 2021-02-17
    相关资源
    最近更新 更多