L010Linux和androidNDK之linux避免僵尸進程,子進程退出的處理
如果你在程序中fork出一個子進程夺蛇,沒有好好處理子進程退出后的相關事宜,那么就有可能召喚出傳說中進程界的僵尸---僵尸進程:ㄕ汀5笊狻!
什么是僵尸進程
一個進程在調用exit命令結束自己的生命的時候闻镶,其實它并沒有真正的被 僵尸進程銷毀甚脉, 而是留下一個稱為僵尸進程(Zombie)的數據結構(系統(tǒng)調用exit,它的作用是 使進程退出铆农,但也僅僅限于將一個正常的進程變成一個僵尸進程牺氨,并不能將其完全銷毀)
僵尸進程是怎么樣產生
在Linux進程的狀態(tài)中,僵尸進程是非常特殊的一種墩剖,它已經放棄了幾乎所有內存空間猴凹,沒有任何可執(zhí)行代碼,也不能被調度岭皂,僅僅在進程列表中保留一個位置郊霎,記載該進程的退出狀態(tài)等信息供其他進程收集,除此之外爷绘,僵尸進程不再占有任何內存空間书劝。它需要它的父進程來為它收尸进倍。
如果他的父進程沒安裝SIGCHLD信號處理函數調用wait或waitpid()等待子進程結束,又沒有顯式忽略該信號购对,那么它就一直保持僵尸狀態(tài)猾昆,如果這時父進程結束了,那么init進程自動會接手這個子進程骡苞,為它收尸垂蜗,它還是能被清除的。
但是如果父進程是一個循環(huán)烙如,不會結束么抗,那么子進程就會一直保持僵尸狀態(tài),這就是為什么系統(tǒng)中有時會有很多的僵尸進程亚铁。系統(tǒng)所能使用的進程號是有限的,如果大量的產生僵死進程,將因為沒有可用的進程號而導致系統(tǒng)不能產生新的進程.
異步回收僵尸進程:
fork()之后蝇刀,子進程從父進程獲取了一份拷貝,和父進程分別獨立運行徘溢,僵尸進程的產生是因為父進程沒有給子進程“收尸”造成的吞琐,又可以根據危害程度分為下述兩類:
總體來說:當子進程結束之后,但父進程未結束之前然爆,子進程將成為僵尸進程站粟。
(1)當子進程結束之后,但父進程未結束之前曾雕,子進程將成為僵尸進程奴烙,父進程結束后僵尸被init進程回收。
(2)如果子進程結束了剖张,但是父進程始終沒有結束切诀,那么這個僵尸將一直存在,而且隨著exec搔弄,僵尸越來越多幅虑。
相關回收進程的函數
#include<sys/types.h>
#include<sys/wait.h>
pid_t waitpid(pid_t pid,int * status,int options);
pid_t wait (int * status);
其中 wait(&status);等價于waitpid(-1, &status, 0);
waitpid()會暫時停止目前進程的執(zhí)行,直到有信號來到或子進程結束
如果在調用 waitpid()時子進程已經結束,則 waitpid()會立即
返回子進程結束狀態(tài)值顾犹。 子進程的結束狀態(tài)值會由參數 status 返回,
而子進程的進程識別碼也會一起返回倒庵。如果不在意結束狀態(tài)值,則
參數 status 可以設成 NULL。參數 pid 為欲等待的子進程識別碼,
其他數值意義如下:
pid<-1 等待進程組識別碼為 pid 絕對值的任何子進程炫刷。
pid=-1 等待任何子進程,相當于 wait()擎宝。
pid=0 等待進程組識別碼與目前進程相同的任何子進程。
pid>0 等待任何子進程識別碼為 pid 的子進程浑玛。
參數options提供了一些額外的選項來控制waitpid认臊,參數 option 可以為 0 或可以用"|"運算符把它們連接起來使用
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <sys/wait.h>
void handler(int num) {
//我接受到了SIGCHLD的信號啦
int status;
int pid = waitpid(-1, &status, WNOHANG);
if (WIFEXITED(status)) {
printf("The child %d exit with code %d\n", pid, WEXITSTATUS(status));
}
}
int main() {
//子進程的pid
int c_pid;
int pid;
signal(SIGCHLD, handler);
if ((pid = fork())) {
//父進程
c_pid = pid;
printf("The child process is %d\n", c_pid);
//父進程不用等待,做自己的事情吧~
int i=0;
for ( i = 0; i < 16; i++) {
printf("Do parent things.\n");
sleep(1);
}
printf("Do parent end\n");
exit(0);
} else {
//子進程
printf("I 'm a child.\n");
sleep(2);
exit(0);
}
}
子進程的結束狀態(tài)
WIFEXITED/WEXITSTATUS/WIFSIGNALED
If the exit status value (*note Program Termination::) of the child
process is zero, then the status value reported by `waitpid' or `wait'
is also zero. You can test for other kinds of information encoded in
the returned status value using the following macros. These macros are
defined in the header file `sys/wait.h'.
-- Macro: int WIFEXITED (int STATUS)
This macro returns a nonzero value if the child process terminated
normally with `exit' or `_exit'.
-- Macro: int WEXITSTATUS (int STATUS)
If `WIFEXITED' is true of STATUS, this macro returns the low-order
8 bits of the exit status value from the child process. *Note
Exit Status::.
-- Macro: int WIFSIGNALED (int STATUS)
This macro returns a nonzero value if the child process terminated
because it received a signal that was not handled. *Note Signal
Handling::
子進程的結束狀態(tài)返回后存于status锄奢,如下有幾個宏可判別結束情況
WIFEXITED(status)如果子進程正常結束則為非0值失晴。
WEXITSTATUS(status)取得子進程exit()返回的結束代碼,一般會先用WIFEXITED 來判斷是否正常結束才能使用此宏拘央。
WIFSIGNALED(status)如果子進程是因為信號而結束則此宏值為真
WTERMSIG(status)取得子進程因信號而中止的信號代碼涂屁,一般會先用WIFSIGNALED 來判斷后才使用此宏。
WIFSTOPPED(status)如果子進程處于暫停執(zhí)行情況則此宏值為真灰伟。一般只有使用WUNTRACED 時才會有此情況拆又。
WSTOPSIG(status)取得引發(fā)子進程暫停的信號代碼,一般會先用WIFSTOPPED 來判斷后才使用此宏栏账。
kill -STOP 1234 進程暫停帖族。
kill -CONT 1234 進程繼續(xù)