使用fork()函式派生出多個子程序來並行執行程式的不同**塊,是一種常用的程式設計泛型。特別是在網路程式設計中,父程序初始化後派生出指定數量的子程序,共同監聽網路埠並處理請求,從而達到擴容的目的。
但是,在使用fork()函式時若處理不當,很容易產生殭屍程序。根據unix系統的定義,殭屍程序是指子程序退出後,它的父程序沒有「等待」該子程序,這樣的子程序就會成為殭屍程序。何謂「等待」?殭屍程序的危害是什麼?以及要如何避免?這就是本文將要闡述的內容。
下面這段c語言**展示了fork()函式的使用方法:
// myfork.c
#include
#include
int main(int argc, char **argv) else
if (pid == 0) else
}}
呼叫fork()函式後,系統會將當前程序的絕大部分資源拷貝乙份(其中的copy-on-write技術這裡不詳述),該函式的返回值有三種情況,分別是:
讓我們編譯執行這段程式,並檢視程序表:
$ gcc myfork.c -o myfork && ./myfork
$ ps -ef | grep fork
vagrant 14860
2748
006:09 pts/0
00:00:00 ./myfork
vagrant 14861
14860
006:09 pts/0
00:00:00 [myfork] vagrant 14864
14860
006:09 pts/0
00:00:00 [myfork] vagrant 14877
14860
006:09 pts/0
00:00:00 [myfork] vagrant 14879
2784
006:09 pts/1
00:00:00 grep fork
可以看到子程序建立成功了,程序號也有對應關係。但是每個子程序後面都跟有「defunct」標識,即表示該程序是乙個殭屍程序。
這段程式會每五秒建立乙個新的子程序,如果不加以**,那就會佔滿程序表,使得系統無法再建立程序。這也是殭屍程序最大的危害。
我們對上面這段程式稍加修改:
pid_t pid = fork();
if (pid > 0) else ...
編譯執行後會發現程序表中不再出現defunct程序了,即子程序已被完全**。因此上文中的「等待」指的是主程序等待子程序結束,獲取子程序的結束狀態資訊,這時核心才會**子程序。
除了通過「等待」來**子程序,主程序退出也會**子程序。這是因為主程序退出後,init程序(pid=1)會接管這些殭屍程序,該程序一定會呼叫wait()函式(或其他類似函式),從而保證殭屍程序得以**。
通常,父程序不會始終處於等待狀態,它還需要執行其它**,因此「等待」的工作會使用訊號機制來完成。
在子程序終止時,核心會傳送sigchld訊號給父程序,因此父程序可以新增訊號處理函式,並在該函式中呼叫wait()函式,以防止殭屍程序的產生。
// myfork2.c
#include
#include
#include
#include
#include
void signal_handler(int signo)
}}void mysleep(int sec)
}int main(int argc, char **argv) else
if (pid == 0) else
}}
**執行結果:
$ gcc myfork2.c -o myfork2 && ./myfork2
child pid 17422
sigchld pid 17422
child pid 17423
sigchld pid 17423
其中,signal()用於註冊訊號處理函式,該處理函式接收乙個signo引數,用來標識訊號的型別。
waitpid()的功能和wait()類似,但提供了額外的選項(wait(null)
等價於waitpid(-1, null, 0)
)。如,wait()函式是阻塞的,而waitpid()提供了wnohang選項,呼叫後會立刻返回,可根據返回值判斷等待結果。
此外,我們在訊號處理中使用了乙個迴圈體,不斷呼叫waitpid(),直到失敗為止。那是因為在系統繁忙時,訊號可能會被合併,即兩個子程序結束只會傳送一次sigchld訊號,如果只wait()一次,就會產生殭屍程序。
最後,由於預設的sleep()函式會在接收到訊號時立即返回,因此為了方便演示,這裡定義了mysleep()函式。
除了在sigchld訊號處理函式中呼叫wait()來避免產生殭屍程序,我們還可以選擇忽略sigchld訊號,告知作業系統父程序不關心子程序的退出狀態,可以直接清理。
signal(sigchld, sig_ign);
但需要注意的是,在部分bsd系統中,這種做法仍會產生殭屍程序。因此更為通用的方法還是使用wait()函式。
perl語言提供了相應的內建函式來建立子程序:
#!/usr/bin/perl
sub reaper
}$sig
= \&reaper;
my$pid = fork();
if ($pid > 0) elsif ($pid == 0)
其思路和c語言基本是一致的。如果想要忽略sigchld,可使用$sig = 'ignore';
,但還是要考慮bsd系統上的限制。 防止殭屍程序的fork程式設計
基本概念 孤兒程序與殭屍程序 原理就是捕獲sigchld訊號,通過waitpid函式處理子程序退出,直接上 gcc fork one.c include include include include include include void sig chld int signo int main ...
兩次fork防止殭屍程序
1 何謂殭屍程序?在linux系統中,乙個已經終止但父程序尚未對其進行善後處理 釋放子程序相關資訊占用的資源 的子程序叫做殭屍程序 子程序結束時,父程序呼叫pid t wait int statloc 或者pid t waitpid pid t pid,int statloc,int options...
fork兩次解決殭屍程序
孤兒程序 孤兒程序是指父程序在子程序結束之前死亡 return 或exit 如下圖1所示 圖1 孤兒程序 但是孤兒程序並不會像上面畫的那樣持續很長時間,當系統發現孤兒程序時,init程序就收養孤兒程序,成為它的父親,child程序exit後的資源 就都由init程序來完成。殭屍程序 殭屍程序是指子程...