2021-05-12 14:32:11
進程管理之wait和waitpid
殭屍進程
在介紹wait、waitpid和waitid函數之前,首先要介紹一下僵屍進程,因為,這三個函數的本質任務就是處理僵屍進程的問題。
進程會我們的生命體一樣,也有消亡。進程在退出時,核心會清理進程幾乎所有的資源。例如:記憶體資源、檔案資源、號誌資源、共用記憶體資源或者參照數減一 又或釋放共用記憶體資源。但還有少量的資源沒有被核心清理,例如:過程控制塊PCB task_struct、核心棧資源。這些資源沒有被釋放,是為了保留一些進程退出是的重要資訊,例如:進程消耗的系統cpu時間、使用者cpu時間;收到了多少信號等待這些資訊,類似於“墓誌銘”,總結了進程的一生。而wait、waitpid和waitid函數就是來釋放這些“墓誌銘”資訊的。之後進程就脫離了殭屍進程的狀態。
進程的僵屍進程的狀態,是一種“刀槍不入的狀態”,即使是用kill -9 也無法殺死。只能通過wait這些函數活著是通過init進程來“收屍”;
對於建立了很多的子進程的父進程,獲取子進程的退出資訊是非常有意義的。
wait函數
函數宣告
include <sys/wait.h>
pid_t wait(int *status);
返回值
- 返回退出子進程的pid;
- 等待過程中,收到了信號。信號打斷了系統呼叫,並且註冊信號處理常式時沒有設定SA_RESTAR標識位,系統呼叫不會被重新啟動。wait函數返回-1,全域性變數error=EINIR(表示函數被信號中斷);
- 所有需要等待的子進程都已經退出,沒有要等的子進程的。wait函數返回-1,全域性變數error=ECHLD(表示呼叫進程時發現並沒有子進程需要等待);
由上述返回值的含義,等待所有子進程的退出時,要注意不要丟掉對信號中斷情況的考慮,程式碼如下:
pit_t my_wait(int *state) { int retval; while( (retval=wait(state))!=-1 && (error==EINIR) ); return retval; }
引數說明
wait函數的引數和waitpid函數的引數是一個意思。但waitpid函數對wait函數的局限性做了擴充套件,所以,在介紹waitpid時,再來講wait的引數。
wait函數的局限性
- 不能等待特定的子進程
- 如果子進程不存在,就會阻塞
- 只能探知子進程的死亡,卻不能探測子進程的暫停,也無法探知進程的甦醒
waitpid函數
waitpid函數的宣告
#include<sys/wait.h>
pid_t waitpid(pit_t pid,int *status,int options);
返回值
返回值和wait函數一樣。
引數說明
pid引數
- pid > 0:表示等待進程ID為pid的子進程;
- pid = 0:表示等待與呼叫進程同一行程群組的任意子進程;
- pid = -1:表示等待任意子進程;
- pid < -1:表示等待所有子進程中,行程群組ID與pid絕對值相等的所有子進程;
pid引數的理解:
首先給父進程要等待的子進程分類。子進程分為:和父進程同一行程群組的子進程,和父進程不同行程群組的子進程。子進程可以設定自己的行程群組,所以某些子進程不一定和父進程歸屬於同一個行程群組;pid>0,很自然,代表要等待的子進程pid,這叫做“精準打擊”;還有情況就是分類等待子進程。由上述分類可知,與父進程為同一行程群組的子進程怎麼表示,呼叫waitpid函數的父進程本來就知道自己的行程群組pid,所以,不用設定引數pid的值,即給0就行;那與父進程不在同一行程群組的子進程的話,就得設定引數pid的值了,但要和“精準打擊”的方式區別開,所以給負值,但取絕對值;當然,等待任意子進程的需求還是有的,而現在也正好只剩下一個值“-1”,剛好給它用;綜述,通過上面的分析才有了pid引數的使用方式。
核心實現簡述
核心之中,wait和waitpid函數呼叫的都是wait4函數。根據pid的值來給wait_ opts結構體變數wo中的wo_type複製,再以實參的形式傳參給do_wait函數,do_wait函數來決定等待什麼狀態的子進程。
wait4中的部分程式碼:
struct wait_opts wo;
.
. //給type複製的過程
.
wo.wo_type = type;
wo.wo_pid = pid;
.
.
.
ret = do_wait(&wo);
在do_ wait函數中,主要要完成兩個人物,第一,父進程中的每個執行緒都會去遍歷子進程,第二,篩選要等待的子進程;在核心中,task_struct成員中children變數是儲存子進程連結串列的連結串列頭,利用list_for_each_enpty函數來遍歷。利用eligible_pid函數來篩選。
遍歷子進程程式碼:
static int do_wait_thread(struct wait_opts *wo,
struct task_struct *tsk)
{
struct task_struct *p;
list_for_each_enpty(p,&tsk->children,silbling)
{
int ret = wait_consider_task(wo,0,p);
if(ret)
return ret;
}
return 0;
}
篩選子進程的程式碼:
static int eligible_pid(struct wait_opts *wo,
struct task_struct *p)
{
return wo->wo_type == PIDTYPE_MAX ||
task_pid_type(p,wo->wo_type) == wo->wo_pid;
}
當waitpid函數引數pid的值為-1時,wo_type的值為PIDTYPE_MAX;其他三種情況由task_pid_type函數來處理。
引數options
是一個位掩碼,可以同時存在多個標誌。當options的值為0時,行為和wait類似。
標誌位:
- WUNTRACE:關心終止子進程和因信號中斷的子進程的資訊(阻塞);
- WCONTINUED:關係終止子進程和信號終止後由恢復執行的子進程(阻塞);
- WNOHANG:指定的子進程沒有發生變化,waitpid立即返回,返回值為0;出錯返回時,返回值為-1,通過error=ECHILD和返回值來區分這兩種情況。注意:error的值不會為EINTR,因為信號中斷由WUTRACE來控制。
引數status
該引數儲存的資訊時按位元儲存的,我們沒辦法解析status的值,只能通過系統提供的宏去解析。這些宏安功能分可以分為兩類:獲取子進程狀態和判斷是非由相應信號產生;
- 進程正常退出
WIFEIXITED(status):正常退出,返回true;
WEXITSTATUS(status):正常退出,獲取進程退出狀態;
- 進程收到信號退出
WIFSIGNALED(status): 被信號殺死,返回true;
WTREMSIG(status):被信號殺死,返回殺死進程的pid;
WCOREDUMOP(status):子進程產生core dump,返回true;
- 進程收到信號停止
WIFSTOPPED(status):收到相關信號,暫停執行,返回true;
WSTOPSIG(status):如果子進程處於停止狀態,該宏返回導致子進
程停止的信號值;
- 進程收到信號回復執行
WIFCONTINUED(status): 遞送SIGCONT信號,子進程回復執行,返回true;
沒有必要返回使子進程甦醒的信號的值,因為只有唯一一個SIGCONT信號才能使進程又停止狀態恢復到執行狀態。
本文永久更新連結地址:http://www.linuxidc.com/Linux/2017-09/146736.htm
相關文章