類UNIX系統中,殭屍行程是指完成執行(通過exit系統呼叫,或執行時發生致命錯誤或收到終止訊號所致),但在作業系統的行程表中仍然存在其過程控制段,處於"終止狀態英語Process state#Terminated"的行程。這發生於子行程需要保留表項以允許其父行程讀取子行程的退出狀態:一旦退出態通過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系統呼叫收割其收養的所有殭屍行程。

為避免產生殭屍行程,實際應用中一般採取的方式是:

  1. 將父行程中對SIGCHLD訊號的處理常式設為SIG_IGN(忽略訊號);
  2. 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;
}

參見

編輯

參考文獻

編輯
  1. ^ Zombies(5) - UNIX System V (Concepts). The Collider Detector at Fermilab. [2014-05-24]. (原始內容存檔於2020-11-09). 
  2. ^ UNIX環境進階編程Advanced Programming in the UNIX Environment),理察·史蒂文斯著,1992,ISBN 0-201-56317-7

外部連結

編輯