行程與行程控制

孤兒行程(orphan process)

孤兒是殭屍的鏡像。殭屍是父行程還在、卻尚未收走的死去子行程。孤兒則是父行程已經死去的存活子行程。這個子行程仍在執行、做著它的工作、好端端的——但建立它的那個行程沒了。用人話說:父母離開了,但孩子還活得好好的,正在上學。

一個父行程結束了的執行中行程會怎樣?它不能被留得無父無母,因為行程樹必須保持連通,而且終究得有人收割它。所以核心收養它:一個被孤立的行程立刻被重新指派給 init(PID 1),它的 PPID 變成 1。你可以看著這件事發生——啟動一個子行程、殺掉父行程,子行程的 PPID 翻成 1,而子行程毫不受擾地繼續跑。子行程通常甚至不會察覺(雖然可以設定成被通知)。當那個孤兒最終結束時,init——它總是在等待它收養的子行程——會收割它,所以一個之後終止的孤兒不會變成卡住的殭屍。孤兒很常見、也完全正常:這正是常駐程式被刻意創造出來的方式,藉由故意 fork、讓父行程結束,好讓子行程被 init 收養、與原本的 shell 脫離。

為何重要:孤兒顯示了行程樹會自我修復——沒有任何執行中的行程會被留得無父,因為 PID 1 把它們全接住。這是重新指派父行程背後的安全網,也是為什麼常駐程式能在啟動它的 shell 之後存活:你啟動它,中介的父行程結束,init 收養那個常駐程式,它便獨立於你的終端機繼續跑。要記牢的關鍵對比:孤兒=父行程沒了、子行程仍存活並被重新指派給 init;殭屍=子行程沒了、父行程仍存活卻尚未收割。不同的問題,相反的方向。

執行:sh -c 'sleep 300 & echo child=$!' —— 外層的 shell 立刻結束,但 sleep 繼續跑。用 ps -o pid,ppid,comm 查它,這個 sleep 的 PPID 現在是 1:核心把這個孤兒重新指派給了 init,等它最終結束時 init 會收割它。

當父行程結束,仍在執行的子行程被重新指派給 PID 1——孤兒就是這樣存活的。

孤兒與殭屍是相反的兩件事,不是同一件。孤兒=父行程死了、子行程存活並被 init 收養。殭屍=子行程死了、父行程存活卻沒呼叫 wait()。把它們搞混會讓你追錯解法。

又稱
orphaned processreparented process失親行程