fclose()/ pclose()可能会阻塞某些文件指针

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()使用.

Sou*_*ity 9

http://linux.die.net/man/3/popen

pclose()函数等待关联的进程终止并返回wait4()返回的命令的退出状态.

由于pclose()想要返回退出状态,它必须等待子进程终止并生成一个.由于fclose()调用pclose(),因此同样适用于fclose().

如果你自己进行fork和exec并做其余的事情,你最终不会直接或间接地调用pclose(),所以在关闭时不会等待.但请注意,除非您的程序设置为忽略SIGCHLD,否则您的进程将不会终止(相反,它会变为僵尸),直到该子进程为止.但至少你的成本会先退出.


Mat*_*ner 8

对目前为止的答案的普遍性感到失望(我可以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().

  • 我做了,11月17日. (2认同)