Mat*_*ner 3 c unix stdio popen fclose
fclose()在dup()其文件描述符块之后调用此处直到子进程结束(可能是因为流已经结束).
FILE *f = popen("./output", "r");
int d = dup(fileno(f));
fclose(f);
Run Code Online (Sandbox Code Playgroud)
但是通过手动进行pipe(),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]);
}
}
Run Code Online (Sandbox Code Playgroud)
为什么会发生这种情况,如何关闭FILE *返回的popen()并在其位置使用文件描述符?
更新:
我知道文档说要使用pclose(),但是fclose()块也是如此.此外,我在glibc代码中搜索,并且pclose()只是调用fclose().该行为是相同的,是否fclose()或pclose()使用.
http://linux.die.net/man/3/popen
pclose()函数等待关联的进程终止并返回wait4()返回的命令的退出状态.
由于pclose()想要返回退出状态,它必须等待子进程终止并生成一个.由于fclose()调用pclose(),因此同样适用于fclose().
如果你自己进行fork和exec并做其余的事情,你最终不会直接或间接地调用pclose(),所以在关闭时不会等待.但请注意,除非您的程序设置为忽略SIGCHLD,否则您的进程将不会终止(相反,它会变为僵尸),直到该子进程为止.但至少你的成本会先退出.
对目前为止的答案的普遍性感到失望(我可以RTFM,tyvm),我已经通过逐步阅读glibc源彻底调查了这一点.
在glibc中pclose()直接调用fclose()没有额外的效果,所以2个调用是相同的.事实上,你可以使用pclose()和fclose()互换.我确信这在进化的实现中纯粹是巧合,并且仍然建议使用pclose()关闭FILE *返回的popen().
神奇的是popen().FILE *S IN的glibc包含有指向相应的函数来处理此类电话的跳转表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
Run Code Online (Sandbox Code Playgroud)
所以缺点是,使用popen(),返回FILE *不能被关闭,即使你是dup()它的文件描述符,因为它将阻塞,直到子进程终止.当然,在此之后,您将留下一个文件描述符到一个管道,该管道将包含子进程在终止之前设置为写入()的任何内容.
如果不fread()使用返回的文件指针popen(),则不会触及底层管道,可以安全地使用文件描述符fileno(),并通过调用完成pclose().