Разочаровавшись в общности ответов (я могу RTFM , tyvm), я тщательно исследовал это, пройдя и прочитав источник glibc .
В glibc pclose()
напрямую звонит fclose()
без дополнительного эффекта, поэтому 2 вызова одинаковы. На самом деле вы можете использовать pclose()
и fclose()
взаимозаменяемо. Я уверен, что это просто совпадение в развитой реализации, и все еще рекомендуется использовать pclose()
для закрытия FILE *
, возвращенного из popen()
.
Магия в popen()
. FILE *
s в 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
Таким образом, используя popen()
, возвращаемый FILE *
не должен закрываться, даже если вы dup()
его файловый дескриптор, потому что он будет блокироваться до завершения дочернего процесса. Конечно, после этого у вас останется дескриптор файла для канала, который будет содержать все, что дочернему процессу удалось записать () в него до завершения.
Если не fread()
с указателем файла, возвращаемым из popen()
, нижележащий канал не будет затронут, можно безопасно использовать дескриптор файла с помощью fileno()
и завершить, вызвав pclose()
.