在陈述我的问题之前,我已经阅读了几个关于堆栈溢出的相关问题,例如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()?从代码的结果来看,它不是。但我错在哪里?
非常感谢你在这里的时间和努力~~
答案 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_pipes
被execlp()
破坏,但这只是程序文件描述符的内部存储。描述符只是内核为进程维护的表的整数索引。 丢失文件描述符值的跟踪不会导致关联的文件被关闭。事实上,这是资源泄漏的一种形式。
从代码的结果来看,它不是。但我错在哪里?
如上所述。
此外,当进程退出时,其所有打开的文件描述符都将关闭,但文件描述符所引用的内核中的基础打开文件描述 ion 仅在没有打开文件时关闭引用它的描述符仍然存在。其他进程可能会保留其他打开文件描述符,因为它们是通过fork()
继承的。
现在关于在执行file_pipes[1]
之前子进程未关闭od
时会发生什么的具体问题,您可以通过ps
命令检查进程列表来获得线索。您将看到子od
进程仍在运行(如果您已经多次测试,可能会有几个)。为什么呢?
那么,od
如何知道何时退出?它处理整个输入,因此它必须在到达输入结束时退出。但是管道输入的结束并不意味着现在没有更多的数据 ,因为以后可能会有更多的数据写入管道的写入端。当写入结束关闭时,管道上的输入结束。如果孩子在执行前没有关闭file_pipes[1]
,那么它可能会无限期地保持开放,因为在执行后,孩子不再知道它拥有它。