Linux C :系统调用-fork,wait,subreaper
? ? ?fork():創(chuàng)建子進(jìn)程,并返回進(jìn)程id。
? ? ?wait(&status):等待子進(jìn)程終止。如果成功則會返回僵尸子進(jìn)程的pid,status的值會是子進(jìn)程的exitcode。
? ? ?exit(value):進(jìn)程正常退出,并返回退出值value
? ? ?prctl(PR_SET_CHILD_SUBREAPER):設(shè)置當(dāng)前進(jìn)程為subreaper進(jìn)程。
零、示例代碼即可能的輸出結(jié)果
目錄
一、fork()
二、wait()
三、subreaper
四、解讀上述代碼和輸出結(jié)果
#include <stdio.h> #include <unistd.h> #include <wait.h> #include <sys/prctl.h> int main(){int pid ,r,status ;printf("mark process %d as a subreaper\n",getpid());r=prctl(PR_SET_CHILD_SUBREAPER);pid = fork();if(pid){//parent printf("subreaper %d child =%d\n",getpid(),pid);while(1){pid =wait(&status);if(pid >0){printf("subreaper %d wait a ZOMBIE=%d\n",getpid(),pid);}else{break;}}}else{//childprintf("child %d parent=%d\n",getpid(),(pid_t)getppid());pid=fork();if(pid){printf("child%d start : grandchild=%d\n",getpid(),pid);printf("child%d exit : grandchild=%d\n",getpid(),pid);}else{printf("grandchild%d start : myparent=%d\n",getpid(),getppid());printf("grandchild%d end : myparent=%d\n",getpid(),getppid());}}}
運(yùn)行結(jié)果:(每次運(yùn)行可能順序不一樣)
? ? ? 在Linux 中,每個用戶在同一時間只能有一定數(shù)量的進(jìn)程。用戶資源限制文件通常在/etc/security/limits.conf 文件中設(shè)置。進(jìn)程有兩種執(zhí)行狀態(tài),一種是內(nèi)核態(tài),一種是用戶態(tài)。每個進(jìn)程都在內(nèi)核中下產(chǎn)生并開始執(zhí)行,想調(diào)用硬件設(shè)備例如操作CPU,鍵盤,或者想執(zhí)行系統(tǒng)調(diào)用都是在內(nèi)核態(tài)下完成的。修改狀態(tài)寄存器的值就可以從內(nèi)核態(tài)轉(zhuǎn)化為用戶態(tài)。一旦轉(zhuǎn)為用戶態(tài),那么就無法直接訪問硬件相關(guān)的設(shè)備,想要重新轉(zhuǎn)為內(nèi)核態(tài)只有3種方式:硬件中斷,異常,系統(tǒng)調(diào)用。 可以說,內(nèi)核是操作系統(tǒng)之下的部分,用戶是操作系統(tǒng)之上的部分。用戶只需關(guān)心操作系統(tǒng)為用戶提供了哪些可訪問內(nèi)核的接口就行。
? ? ? 在內(nèi)核模式中,每個進(jìn)程都有完全相同的代碼段,數(shù)據(jù)段和堆,但是每個進(jìn)程都有只屬于自己的棧。
一、fork()
? ? ?創(chuàng)建子進(jìn)程并返回子進(jìn)程的pid , int pid = fork();? ?如果fork失敗則為-1。當(dāng)創(chuàng)建了子進(jìn)程后,子進(jìn)程和父進(jìn)程在用戶態(tài)的映像完全相同,但是在內(nèi)核態(tài)下的映像只有棧有區(qū)別,意味著子進(jìn)程和父進(jìn)程有相同的代碼和數(shù)據(jù)。因為創(chuàng)建子進(jìn)程時用戶態(tài)的棧也是一樣的,意味著棧幀情況也是一樣的。創(chuàng)建完后子進(jìn)程和父進(jìn)程都從fork()后繼續(xù)執(zhí)行.fork()方法調(diào)用了內(nèi)核中kfork()函數(shù),它對創(chuàng)建出來的子進(jìn)程返回是0,父進(jìn)程返回值是子進(jìn)程的進(jìn)程號;
int pid =fork(); if(pid=-1){//父進(jìn)程創(chuàng)建子進(jìn)程失敗的分支 }else if(pid ==0){//子進(jìn)程執(zhí)行的分支,因為子進(jìn)程返回的pid是0 }else {//父進(jìn)程執(zhí)行的分支,pid是子進(jìn)程的id }二、wait()
? ? ? ?等待僵尸子進(jìn)程。所謂的僵尸進(jìn)程,就是程序已經(jīng)結(jié)束但是資源未釋放的進(jìn)程。wait除了返回僵尸子進(jìn)程的id,獲取子進(jìn)程的退出狀態(tài),還會釋放僵尸子進(jìn)程。wait系統(tǒng)調(diào)用將調(diào)用內(nèi)核中的kwait函數(shù)
三、subreaper
進(jìn)程可以用系統(tǒng)調(diào)用定義自己為subreaper進(jìn)程:
prctl(PR_SET_CHILD_SUBREAPER);
? ? ? 如果子進(jìn)程的父進(jìn)程先死亡,那么該子進(jìn)程就會成為一個孤兒進(jìn)程。在Linux中,標(biāo)記為subreaper的祖先進(jìn)程會成為該孤兒的父進(jìn)程,否則將成為P1進(jìn)程子進(jìn)程。(P1進(jìn)程是操作系統(tǒng)初始化的進(jìn)程,除P0和P1進(jìn)程,其他所有進(jìn)程都是P1進(jìn)程的子孫進(jìn)程)。不過要注意的是,當(dāng)P1清理重定父級進(jìn)程時,會丟失關(guān)于子進(jìn)程的所有信息。P1因為進(jìn)程等級高,當(dāng)P1作為孤兒進(jìn)程的父進(jìn)程時服務(wù)管理器不能再從服務(wù)守護(hù)進(jìn)程中接收SIGCHLD信號,也不能等待任何僵尸子進(jìn)程。定義subreaper就可以很好的解決上述問題,并且可以有效地減少P1進(jìn)程的工作量。
顯示當(dāng)前用戶的subreaper 進(jìn)程的PID 和信息:
? ? ?ps fxau | grep USERNAME | grep "/sbin/upstart"
四、解讀上述代碼和輸出結(jié)果
? 就運(yùn)行結(jié)果而言,
1)當(dāng)fork創(chuàng)建出來子進(jìn)程后,父進(jìn)程獲取到CPU使用權(quán)先執(zhí)行,并打印出了
? ? ??
? ? ? 然后進(jìn)入while循環(huán)等待子進(jìn)程執(zhí)行結(jié)束。
2)接下來子進(jìn)程執(zhí)行并打印出了,
? ? ??
? ? ?之后子進(jìn)程 fork 創(chuàng)建出了一個子進(jìn)程的子進(jìn)程即孫子進(jìn)程。
3)子進(jìn)程先執(zhí)行完畢,并打印出:
? ? ??
? ? ? 此時,子進(jìn)程執(zhí)行完畢但是未釋放進(jìn)程資源,于是成為了僵尸進(jìn)程。
4)此時父進(jìn)程獲取到CPU使用權(quán),通過wait返回子進(jìn)程的pid,繼續(xù)執(zhí)行打印出了??
? ? ?
? ? ?wait釋放了子進(jìn)程的資源,使孫子進(jìn)程成為了孤兒進(jìn)程,但是由于之前父進(jìn)程(pid=3434)將自己設(shè)置成了PR_SET_CHILD_SUBREAPER? , 使 孫子進(jìn)程(pid=3436)的父親不再是已經(jīng)死亡的(pid=3435),而是父進(jìn)程(pid=3434)。
5)所以當(dāng)孫子執(zhí)行完自己的代碼后,打印出
? ? ?成為了僵尸進(jìn)程后,被父進(jìn)程 wait() 釋放出來,打印出
? ? 后結(jié)束。
與50位技術(shù)專家面對面20年技術(shù)見證,附贈技術(shù)全景圖總結(jié)
以上是生活随笔為你收集整理的Linux C :系统调用-fork,wait,subreaper的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux C : 进程管理实验:创建进
- 下一篇: Linux C:文件描述符、IO重定向、