2009-11-15 59 views
3

dup()之後調用fclose()此文件描述符塊直到子進程結束(可能是因爲流已結束)。fclose()/ pclose()可能在某些文件指針上阻塞

FILE *f = popen("./output", "r"); 
int d = dup(fileno(f)); 
fclose(f); 

然而,通過手動執行pipe()fork(),所述popen()execvp(),然後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(),但是fclose()塊爲好。此外,我在glibc代碼中探索,pclose()只需撥打fclose()。行爲是相同的,無論是使用fclose()還是pclose()

+0

你知道爲什麼從'popen()'塊關閉'FILE *'的唯一方法。也許你可以多解釋一下你的問題的一部分還沒有答案? – Andomar 2009-11-20 09:30:38

回答

7

對迄今爲止答案的普遍性感到失望(我可以RTFM,tyvm),我已經仔細研究過了,通過閱讀glibc來源。

在glibc pclose()直接調用fclose()沒有額外的效果,所以這兩個調用是相同的。實際上,您可以互換使用pclose()fclose()。我確信這在演進的實現中純粹是巧合,並且仍然推薦使用pclose()來關閉從popen()返回的FILE *

魔法在popen()。 glibc中的FILE *包含跳轉表,指針指向適當的函數以處理諸如fseek(),fread()和相關性fclose()的呼叫。當撥打popen()時,會使用與fopen()使用的跳轉表不同的跳轉表。此跳轉表中的close成員指向一個特殊功能_IO_new_proc_close,該功能在存儲在FILE *指向的區域中的pid上調用waitpid()

下面是我的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()返回的文件指針ING,底層管道不會被觸動,它是安全的fileno()使用文件描述符,並通過調用pclose()完成了。

+0

我提到了上面的代碼指針數組。這不僅僅是glibc,它永遠都是在每個UNIX標準C庫中。 有多奇怪,你可以爲自己的問題寫一個答案,並接受它作爲答案?哇,每個人都有即時的業力。 – 2009-11-20 17:02:33

+0

如果你知道這一點,你可以回答這個問題。 – 2009-11-20 23:14:26

+2

我在11月17日做過。 – 2009-11-21 03:44:00

1

FILE*popen()返回應關閉pclose()而不是fclose()。然後,對於pclose()的文檔指定:

的函數,pclose()函數等待 相關聯的過程來終止與 返回通過wait4()返回的命令 的退出狀態。

所以等待是兩個東西pclose()做,除了關閉文件描述符之一。 close()只做一件事:它關閉描述符。

針對你的第二個問題,我認爲你可以使用fileno()返回的描述符。沒有必要dup()它。當你完成它後,pclose()原來。

+0

@Andomar:我知道文檔說要使用pclose(),但是''fclose()'塊也是**。此外,我在libc代碼中搜索了一下,'pclose()'只是調用'fclose()'。 – 2009-11-15 12:46:41

+2

@Anacrolix:從'popen()'在'FILE *'中調用'fclose()'是未定義的行爲。你的libc可以做任何事情:阻塞,打印錯誤信息或格式化磁盤 – Andomar 2009-11-15 13:06:26

+0

@Andomar,我很清楚這一點。我使用'fclose()'/'pclose()'比較指出等待行爲似乎是'fclose()'調用特有的。 – 2009-11-15 13:23:00

10

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

的函數,pclose()函數等待關聯的進程終止並返回命令的退出狀態通過wait4()返回。

因爲pclose()想要返回退出狀態,所以必須等待子進程終止並生成一個。由於fclose()調用pclose(),因此您也可以使用fclose()。

如果你fork和exec並且自己完成剩下的工作,你不會最終調用pclose()(直接或間接),所以在關閉時沒有等待。但是請注意,除非你的程序被設置爲忽略SIGCHLD,否則你的程序將不會終止(反而會變殭屍),直到孩子完成。但至少你的成本將首先退出。

+0

'pclose()'調用'fclose()',而不是相反。 – 2009-11-16 05:57:52

+0

無論如何。在你的情況下,兩者都將導致等待。因爲據我所知,在UNIX中,FILE *實際上指向函數指針數組。顯然,來自popen()的FILE *中的函數指針會等待孩子完成,因此它可以獲得返回值。 – 2009-11-17 01:32:14

+0

+1這是爲什麼被拒絕?這是正確的,有幫助的,並且對於 – Andomar 2009-11-20 10:05:20

相關問題