2012-01-23 36 views
7

我瞭解到: 1)waitpid用於等待孩子的死亡,然後收集SIGCHLD和孩子的退出狀態等。 2)當我們有一個SIGCHLD的信號處理程序時,我們會做更多的事情有關清理孩子或其他東西(直到程序員),然後做一個waitpid,使孩子不會殭屍,然後返回。使用waitpid或sigaction?

現在,當我們執行fork/exec並且子進程返回時,我們是否需要在程序中同時使用1和2? 如果我們有兩個,首先獲得了SIGCHLD,所以信號處理程序被稱爲第一,因此,其waitpid函數調用成功,而不是在父進程的代碼waitpid函數如下:

my_signal_handler_for_sigchld 
{ 
do something 
tmp = waitpid(-1,NULL,0); 
print tmp (which is the correct value of the child pid) 
} 

int main() 
{ 
    sigaction(SIGCHLD, my_signal_handler_for_sigchld) 
    fork() 
    if (child) //do something, return 
    if parent // waitpid(child_pid, NULL,0); print value returned from this waitpid - it is -1 
} 

感激,如果有人可以幫助我瞭解這個。

回答

1

您需要撥打等待系統調用如waitpid或朋友-eg wait4等,否則您可能有zombie processes

您可以處理SIGCHLD以通知某個孩子結束(或停止等),但您需要稍後再等待。

信號處理程序僅限於調用一小組異步信號安全函數(有關更多信息,請參見signal(7))。好的建議是在內部設置一個volatile sig_atomic_t標誌,然後在更安全的地方進行測試。

18

你真的不需要處理SIGCHLD如果你的目的是運行一個子進程,做一些東西,然後等待它完成。在這種情況下,只需在準備好進行同步時撥打waitpid即可。 SIGCHLD唯一有用的是異步通知子終止,例如,如果您有一個交互式(或長時間運行的守護進程)應用程序,它可以產生各種子代,並且需要知道它們何時完成。然而,SIGCHLD對於這個目的也是非常糟糕/醜陋的,因爲如果你使用的是創建子進程的庫代碼,你可能會捕獲圖書館子進程終止的事件並干擾它的處理。信號處理程序本質上是過程全局的,處理全局狀態,這通常是一件壞事(tm)。

下面是當你有孩子過程中二更好的方法將被異步終止:

方法1(select/poll基於事件的):請確保您有/從每個子進程創建一個管道。它可以是他們的stdin/stdout/stderr或者只是一個額外的虛擬fd。當子進程終止時,管道的末端將被關閉,並且主事件循環將檢測該文件描述符上的活動。從它關閉的事實中,您認識到子進程已經死亡,並致電waitpid收穫殭屍。

方法2(基於線程):對於您創建的每個子進程,還創建一個線程,該線程將立即在子進程的PID上調用waitpid。當waitpid成功返回時,使用您最喜歡的線程同步原語來讓程序的其餘部分知道該子節點已終止,或者只需在該節點線程結束之前照顧您需要執行的所有操作。

這兩種方法都是模塊化和圖書館友好的(它們避免干擾可能利用子進程的代碼或庫代碼的任何其他部分)。

+0

嗨,謝謝你的回答。但是我真正想要的是:當我在父進程中同時使用sig_handler和waitpid時,sig_handler被稱爲sigchld,然後第二個waitpid如上面在我的問題中所示返回-1。那麼我可以刪除我正在使用的第二個waitpid嗎? – Vin

+0

是的,您只能成功等待給定的子進程一次。這實際上是一個等待,因爲pid被第一次等待「釋放」,並且可能已被重新用於新的子進程(如果您恰好創建了另一個進程)。 –

+0

@R ..我們可以澄清一下'當子進程終止時,它的管道末端將會關閉嗎?準確地說,是不是'當子進程關閉與管端連接的FD時,並且沒有其他進程將FD連接到該管端,那麼管道的另一端將是表示已關閉(接收EOF)'?我相信這將更準確地解釋發生了什麼,以及如果超過2個具有FD的流程打開管道會發生什麼情況的重要特例。 – nh2