fork 與殭屍程序

2021-08-08 02:28:45 字數 2885 閱讀 3135

使用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程序來完成。殭屍程序 殭屍程序是指子程...