在fclose()
其文件描述符块之后调用dup()
,直到子进程结束(可能是因为流已经结束)。
FILE *f = popen("./output", "r");
int d = dup(fileno(f));
fclose(f);
但是,通过手动执行pipe()
的{{1}},fork()
,execvp()
,然后popen()
管道的读取文件描述符,关闭原始文件不会阻止。
dup()
为什么会发生这种情况,如何关闭从int p[2];
pipe(p);
switch (fork()) {
case 0: {
char *argv[] = {"./output", NULL};
close(p[0]);
dup2(p[1], 1);
execvp(*argv, argv);
}
default: {
close(p[1]);
int d = dup(p[0]);
close(p[0]);
}
}
返回的FILE *
并在其位置使用文件描述符?
更新
我知道文档说要使用popen()
,但pclose()
也会阻止。此外,我在glibc代码中搜索过,fclose()
只调用pclose()
。无论使用fclose()
还是fclose()
,行为都是相同的。
答案 0 :(得分:9)
http://linux.die.net/man/3/popen
pclose()函数等待关联的进程终止并返回wait4()返回的命令的退出状态。
由于pclose()想要返回退出状态,它必须等待子进程终止并生成一个。由于fclose()调用了pclose(),因此同样适用于fclose()。
如果你自己分叉并执行其余的操作,那么你最终不会直接或间接地调用pclose(),所以在关闭时不会等待。但请注意,除非您的程序设置为忽略SIGCHLD,否则您的进程将不会终止(相反,它会变为僵尸),直到该子进程为止。但至少你的成本会先退出。
答案 1 :(得分:8)
对目前为止的答案的普遍性感到失望(我可以RTFM,tyvm),我通过逐步阅读glibc来源彻底调查了这一点。
在glibc中pclose()
直接调用fclose()
而没有其他影响,因此2次调用是相同的。实际上,您可以互换使用pclose()
和fclose()
。我确信这在演进的实现中纯粹是巧合,并且仍然建议使用pclose()
来关闭从FILE *
返回的popen()
。
魔法在popen()
。 glibc中的FILE *
包含一个跳转表,其中包含指向适当函数的指针,以处理fseek()
,fread()
和相关fclose()
等调用。调用popen()
时,使用的跳转表与fopen()
使用的跳转表不同。此跳转表中的close
成员指向一个特殊函数_IO_new_proc_close
,该函数调用存储在waitpid()
指向的区域中的pid上的FILE *
。
这是我的glibc版本中的相关调用堆栈,我已经注释了有关正在发生的事情的注释:
// linux waitpid system call interface
#0 0x00f9a422 in __kernel_vsyscall ()
#1 0x00c38513 in __waitpid_nocancel () from /lib/tls/i686/cmov/libc.so.6
// removes fp from a chain of proc files
// and waits for the process of the stored pid to terminate
#2 0x00bff248 in _IO_new_proc_close (fp=0x804b008) at iopopen.c:357
// flushes the stream and calls close in its jump table
#3 0x00c09ff3 in _IO_new_file_close_it (fp=0x804b008) at fileops.c:175
// destroys the FILEs buffers
#4 0x00bfd548 in _IO_new_fclose (fp=0x804b008) at iofclose.c:62
// calls fclose
#5 0x00c017fd in __new_pclose (fp=0x804b008) at pclose.c:43
// calls pclose
#6 0x08048788 in main () at opener.c:34
所以缺点是,使用popen()
,即使您FILE *
其文件描述符,也不能关闭返回的dup()
,因为它将阻塞,直到子进程终止。当然,在此之后你将留下一个文件描述符给一个管道,它将包含子进程在终止之前设法写入的所有内容。
不fread()
使用popen()
返回的文件指针,不会触及底层管道,fileno()
使用文件描述符是安全的,并通过调用完成pclose()
。
答案 2 :(得分:1)
FILE*
返回的popen()
应由pclose()
关闭,而不是由fclose()
关闭。然后,pclose()
的文档指定:
pclose()函数等待 终止和关联的过程 返回命令的退出状态 由wait4()返回。
因此,除了关闭文件描述符之外,等待是pclose()
做的两件事之一。 close()
只做一件事:它关闭描述符。
在回答您的第二个问题时,我认为您可以使用fileno()
返回的描述符。没有dup()
它。完成后,pclose()
原始文件。