殭屍進程
在類UNIX系統中,殭屍進程是指完成執行(通過exit
系統調用,或運行時發生致命錯誤或收到終止信號所致),但在操作系統的進程表中仍然存在其進程控制塊,處於"終止狀態"的進程。這發生於子進程需要保留表項以允許其父進程讀取子進程的退出狀態:一旦退出態通過wait
系統調用讀取,殭屍進程條目就從進程表中刪除,稱之為"回收"(reaped)。正常情況下,進程直接被其父進程wait
並由系統回收。進程長時間保持殭屍狀態一般是錯誤的並導致資源泄漏。
「殭屍進程」的各地常用名稱 | |
---|---|
中國大陸 | 殭屍進程 |
臺灣 | 殭屍行程 |
港澳 | 殭屍行程 |
英文術語zombie process源自喪屍——不死之人,隱喻子進程已死但仍然沒有被回收。與正常進程不同,kill
命令對殭屍進程無效。孤兒進程不同於殭屍進程,其父進程已經死掉,但孤兒進程仍能正常執行,但並不會變為殭屍進程,因為被init
(進程ID號為1)收養並wait
其退出。
子進程死後,系統會發送SIGCHLD信號給父進程,父進程對其默認處理是忽略。如果想響應這個消息,父進程通常在信號事件處理程序中,使用wait
系統調用來響應子進程的終止。
殭屍進程被回收後,其進程號與在進程表中的表項都可以被系統重用。但如果父進程沒有調用wait
,殭屍進程將保留進程表中的表項,導致了資源泄漏。某些情況下這反倒是期望的:父進程創建了另外一個子進程,並希望具有不同的進程號。如果父進程通過設置事件處理函數為SIG_IGN
顯式忽略SIGCHLD信號,而不是隱式默認忽略該信號,或者具有SA_NOCLDWAIT
標誌,所有子進程的退出狀態信息將被拋棄並且直接被系統回收。
UNIX命令ps
列出的進程的狀態("STAT")欄標示為 "Z
"則為殭屍進程。[1]
收割殭屍進程的方法是通過kill
命令手工向其父進程發送SIGCHLD信號。如果其父進程仍然拒絕收割殭屍進程,則終止父進程,使得init
進程收養殭屍進程。init
進程周期執行wait
系統調用收割其收養的所有殭屍進程。
為避免產生殭屍進程,實際應用中一般採取的方式是:
- 將父進程中對SIGCHLD信號的處理函數設為SIG_IGN(忽略信號);
- fork兩次並殺死一級子進程,令二級子進程成為孤兒進程而被init所「收養」、清理[2]。
例子
#include <sys/wait.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
pid_t pids[10];
int i;
for (i = 9; i >= 0; --i) {
pids[i] = fork();
if (pids[i] == 0) {
sleep(i+1);
_exit(0);
}
}
for (i = 9; i >= 0; --i)
waitpid(pids[i], NULL, 0);
return 0;
}
參見
參考文獻
- ^ Zombies(5) - UNIX System V (Concepts). The Collider Detector at Fermilab. [2014-05-24]. (原始內容存檔於2020-11-09).
- ^ UNIX環境高級編程(Advanced Programming in the UNIX Environment),理查德·史蒂文斯著,1992,ISBN 0-201-56317-7
- UNIX man pages : ps (). UNIXhelp for Users. [2014-05-24]. (原始內容存檔於2013-03-08).