Linux的exit()是進程退出用的,wait()則是父進程用來回收已退出的子進程的資源的。
進程已經調用了exit()退出,而父進程還沒有調用wait()回收資源的狀態,就是殭屍狀態(task zombie)。這時該進程已經不能運行,但還佔著待回收資源。
先看看這兩個函數的man手冊介紹,
exit的參數是退出狀態碼,一般正常退出傳0,出錯退出傳-1,無返回值,因為進程調用了exit()之後就不再返回了。
wait的參數是返回子進程退出狀態的指針,是個傳出參數,返回值是wait到的子進程的pid。
這兩個模擬一下。
先給我們的task_t加兩個成員變量,exit_code表示進程的退出碼,status表示進程的運行狀態。
把他們加在結構體的尾部,這樣不會影響到之前變量的偏移量。
定義兩個進程狀態,TASK_RUN和TASK_ZOMBIE,只有TASK_RUN時才可以被調度執行。
定義SIG_CHILD信號,在子進程退出時發送給父進程。
下圖為exit的實現t_exit()函數,為了簡單依然把task_t的指針傳過來,而不是拿著進程號去找它。
退出碼固定設為-1,設置進程狀態為TASK_ZOMBIE,這時它已經不會被再次調度運行了。
給父進程發送SIG_CHILD信號,然後調用t_schedule()函數進行進程切換,再也沒法切換回來了。
因為我們只有2個進程,0號是父進程不能退出,只能1號子進程退出,省略了進程選擇機制,直接切換到0號進程執行。
然後,切換到父進程執行,先進行信號處理,如果是SIG_CHILD信號則使用wait()回收資源並獲取子進程的退出信息。
t_wait()函數先判斷子進程的狀態,如果是TASK_ZOMBIE則讀取它的退出碼,然後把id設為-1,status設為0。
這時這個task_t結構就可以再次使用了。
我們在t_fork()裡查找可用的task_t結構是根據它的id為-1來判斷可用的。
在Linux裡,子進程調用exit(),它的用戶空間的內存、文件描述符等資源是它自己在內核態清理的。清理完畢之後給父進程發信號SIG_CHILD,然後執行schedule()放棄CPU,切換到其他進程。
子進程沒法自己清理的是它的task_struct結構,這個結構在內核裡表示該進程本身,它沒法在運行時釋放這個結構,必須先把自己設置為TASK_ZOMBIE狀態,通知父進程來回收。
下圖為main函數,和之前的差不多,加了行設置0號進程的狀態的代碼,列印了兩個進程的棧底信息。
t0_run()函數的代碼,在子進程退出之後會再次把它fork出來,如果兩進程都在運行則各自列印信息,子進程3秒後退出,如此循環運行。
這是運行的效果,子進程退出之後,父進程會再次用t_fork()把它創建出來,具體代碼在t0_run()函數裡。
rsp顯示的是父子兩進程的棧指針,輪流切換。