在 UNIX 裡,除了程式 0(即 PID=0 的交換程式,Swapper Process)以外的所有程式都是由其他程式使用系統呼叫 fork 建立的,這裡呼叫 fork 建立新程式的程式即為父程式,而相對應的為其建立出的程式則為子程式,因而除了程式 0 以外的程式都只有一個父程式,但一個程式可以有多個子程式。
作業系統核心以程式識別符號(Process Identifier,即 PID)來識別程式。程式 0 是系統引導時建立的一個特殊程式,在其呼叫 fork 建立出一個子程式(即 PID=1 的程式 1,又稱 init)後,程式 0 就轉為交換程式(有時也被稱為空閒程式),而程式 1(init 程式)就是系統裡其他所有程式的祖先。
一、殭屍程式
1 、什麼是殭屍程式?
當一個子程式結束執行(一般是呼叫 exit 、執行時發生致命錯誤或收到終止訊號所導致)時,子程式的退出狀態(返回值)會回報給作業系統,系統則以 SIGCHLD 訊號將子程式被結束的事件告知父程式,此時子程式的程式控制塊(PCB)仍駐留在記憶體中。一般來說,收到 SIGCHLD 後,父程式會使用 wait 系統呼叫以取得子程式的退出狀態,然後核心就可以從記憶體中釋放已結束的子程式的 PCB;而如若父程式沒有這麼做的話,子程式的 PCB 就會一直駐留在記憶體中,也即成為殭屍程式。
2 、殭屍程式的危害
由於子程式的結束和父程式的執行是一個非同步過程, 即父程式永遠無法預測子程式 到底什麼時候結束. 那麼不會因為父程式太忙來不及 wait 子程式, 或者說不知道子程式什麼時候結束, 而丟失子程式結束時的狀態資訊呢? 不會. 因為 UNIX 提供了一種機制可以保證 只要父程式想知道子程式結束時的狀態資訊, 就可以得到. 這種機制就是: 在每個程式退出的時候, 核心釋放該程式所有的資源, 包括開啟的檔案, 佔用的記憶體等. 但是仍然為其保留一定的資訊 (包括程式號 the process ID, 退出狀態 the termination status of the process, 執行時間 the amount of CPU time taken by the process 等), 直到父程式透過 wait / waitpid 來取時才釋放. 但這樣就導致了問題, 如果你程式不呼叫 wait / waitpid 的話, 那麼保留的那段資訊就不會釋放, 其程式號就會一定被佔用, 但是系統所能使用的程式號是有限的, 如果大量的產生僵死程式, 將因為沒有可用的程式號而導致系統不能產生新的程式. 此即為殭屍程式的危害應當避免。
3 、殭屍程式的清理
為避免產生殭屍程式,實際應用中一般採取的方式是:
1)將父程式中對 SIGCHLD 訊號的處理函式設為 SIG_IGN(忽略訊號);
2)fork 兩次並殺死一級子程式,令二級子程式成為孤兒程式而被 init 所 “收養” 、清理。
二、 孤兒程式
1 、什麼是孤兒程式?
孤兒程式則是指父程式結束後仍在執行的子程式。在類 UNIX 系統中,孤兒程式一般會被 init 程式所 “收養”,成為 init 的子程式。
2 、孤兒程式的危害
孤兒程式是沒有父程式的程式,孤兒程式這個重任就落到了 init 程式身上,init 程式就好像是一個民政局,專門負責處理孤兒程式的善後工作。每當出現一個孤兒程式的時候,核心就把孤 兒程式的父程式設定為 init,而 init 程式會迴圈地 wait() 它的已經退出的子程式。這樣,當一個孤兒程式淒涼地結束了其生命週期的時候,init 程式就會代表黨和政府出面處理它的一切善後工作。因此孤兒程式並不會有什麼危害。
三、 Linux 中的程式基本狀態:
1 、執行 (R) 狀態:CPU 正在執行,即程式正在佔用 CPU 。
2 、就緒 (W) 狀態:程式已經具備的執行的一切條件,正在等待分配 CPU 的處理時間片。
3 、停止 (S) 狀態:程式不能使用 CPU 。
四、三個函式:
fork(); 功能: 建立一個新的程式。(fork()<0[出錯]、fork()==0[子程式]、fork()>0[父程式]
wait(); 功能:真正結束程式(收屍)。
exec(); 功能:執行外部程式。
孤兒程式與殭屍程式的區別:
殭屍程式會造成資源浪費,孤兒程式則不會!