當(dāng)前位置:首頁(yè) > 嵌入式培訓(xùn) > 嵌入式學(xué)習(xí) > 講師博文 > 孤兒進(jìn)程和僵尸進(jìn)程
前段時(shí)間,由于研究經(jīng)典面試題,把孤兒進(jìn)程和僵尸進(jìn)程也總結(jié)了一下。
我們有這樣一個(gè)問(wèn)題:孤兒進(jìn)程和僵尸進(jìn)程,怎么產(chǎn)生的?有什么危害?怎么去預(yù)防?
下面是針對(duì)此問(wèn)題的總結(jié)與概括。
一.產(chǎn)生的原因
1) 一般進(jìn)程
正常情況下:子進(jìn)程由父進(jìn)程創(chuàng)建,子進(jìn)程再創(chuàng)建新的進(jìn)程。父子進(jìn)程是一個(gè)異步過(guò)程,父進(jìn)程永遠(yuǎn)無(wú)法預(yù)測(cè)子進(jìn)程的結(jié)束,所以,當(dāng)子進(jìn)程結(jié)束后,它的父進(jìn)程會(huì)調(diào)用wait()或waitpid()取得子進(jìn)程的終止?fàn)顟B(tài),回收掉子進(jìn)程的資源。
2)孤兒進(jìn)程
孤兒進(jìn)程:父進(jìn)程結(jié)束了,而它的一個(gè)或多個(gè)子進(jìn)程還在運(yùn)行,那么這些子進(jìn)程就成為孤兒進(jìn)程(father died)。子進(jìn)程的資源由init進(jìn)程(進(jìn)程號(hào)PID = 1)回收。
3)僵尸進(jìn)程
僵尸進(jìn)程:子進(jìn)程退出了,但是父進(jìn)程沒(méi)有用wait或waitpid去獲取子進(jìn)程的狀態(tài)信息,那么子進(jìn)程的進(jìn)程描述符仍然保存在系統(tǒng)中,這種進(jìn)程稱為僵死進(jìn)程。
二.問(wèn)題危害
注意:unix提供了一種機(jī)制保證父進(jìn)程知道子進(jìn)程結(jié)束時(shí)的狀態(tài)信息。
這種機(jī)制是:在每個(gè)進(jìn)程退出的時(shí)候,內(nèi)核會(huì)釋放所有的資源,包括打開(kāi)的文件,占用的內(nèi)存等。但是仍保留一部分信息(進(jìn)程號(hào)PID,退出狀態(tài),運(yùn)行時(shí)間等)。直到父進(jìn)程通過(guò)wait或waitpid來(lái)取時(shí)才釋放。
但是這樣就會(huì)產(chǎn)生問(wèn)題:如果父進(jìn)程不調(diào)用wait或waitpid的話,那么保留的信息就不會(huì)被釋放,其進(jìn)程號(hào)就會(huì)被一直占用,但是系統(tǒng)所能使用的進(jìn)程號(hào)是有限的,如果大量產(chǎn)生僵死進(jìn)程,將因沒(méi)有可用的進(jìn)程號(hào)而導(dǎo)致系統(tǒng)無(wú)法產(chǎn)生新的進(jìn)程,這就是僵尸進(jìn)程的危害
孤兒進(jìn)程是沒(méi)有父進(jìn)程的進(jìn)程,它由init進(jìn)程循環(huán)的wait()回收資源,init進(jìn)程充當(dāng)父進(jìn)程。因此孤兒進(jìn)程并沒(méi)有什么危害。
補(bǔ)充:任何一個(gè)子進(jìn)程(init除外)在exit()之后,并非馬上就消失掉,而是留下一個(gè)稱為僵尸進(jìn)程的數(shù)據(jù)結(jié)構(gòu),等待父進(jìn)程去處理。如果父進(jìn)程在子進(jìn)程exit()之后,沒(méi)有及時(shí)處理,出現(xiàn)僵尸進(jìn)程,并可以用ps命令去查看,它的狀態(tài)是“Z”。
三.解決方案
1)kill殺死元兇父進(jìn)程(一般不用)
嚴(yán)格的說(shuō),僵尸進(jìn)程并不是問(wèn)題的根源,罪魁禍?zhǔn)资钱a(chǎn)生大量僵死進(jìn)程的父進(jìn)程。因此,我們可以直接除掉元兇,通過(guò)kill發(fā)送SIGTERM或者SIGKILL信號(hào)。元兇死后,僵尸進(jìn)程進(jìn)程變成孤兒進(jìn)程,由init充當(dāng)父進(jìn)程,并回收資源。
或者運(yùn)行:kill -9 父進(jìn)程的pid值、
2)父進(jìn)程用wait或waitpid去回收資源(方案不好)
父進(jìn)程通過(guò)wait或waitpid等函數(shù)去等待子進(jìn)程結(jié)束,但是不好,會(huì)導(dǎo)致父進(jìn)程一直等待被掛起,相當(dāng)于一個(gè)進(jìn)程在干活,沒(méi)有起到多進(jìn)程的作用。
3)通過(guò)信號(hào)機(jī)制,在處理函數(shù)中調(diào)用wait,回收資源
通過(guò)信號(hào)機(jī)制,子進(jìn)程退出時(shí)向父進(jìn)程發(fā)送SIGCHLD信號(hào),父進(jìn)程調(diào)用signal(SIGCHLD,sig_child)去處理SIGCHLD信號(hào),在信號(hào)處理函數(shù)sig_child()中調(diào)用wait進(jìn)行處理僵尸進(jìn)程。什么時(shí)候得到子進(jìn)程信號(hào),什么時(shí)候進(jìn)行信號(hào)處理,父進(jìn)程可以繼續(xù)干其他活,不用去阻塞等待。
例子1:
#include
#include
#include
#include
#include
static void sig_child(int signo);
int main()
{
pid_t pid;
//創(chuàng)建捕捉子進(jìn)程退出信號(hào)
signal(SIGCHLD,sig_child);
pid = fork();
if (pid < 0)
{
perror("fork error:");
exit(1);
}
else if (pid == 0)
{
printf("I am child process,pid id %d.I am exiting.\n",getpid());
exit(0);
}
printf("I am father process.I will sleep two seconds\n");
//等待子進(jìn)程先退出
sleep(2);
//輸出進(jìn)程信息
system("ps -o pid,ppid,state,tty,command");
printf("father process is exiting.\n");
return 0;
}
static void sig_child(int signo)
{
pid_t pid;
int stat;
//處理僵尸進(jìn)程
while ((pid = waitpid(-1, &stat, WNOHANG)) >0)
printf("child %d terminated.\n", pid);
}
4)fork兩次
fork兩次,父進(jìn)程fork一個(gè)子進(jìn)程,子進(jìn)程在fork出一個(gè)孫子進(jìn)程,然后子進(jìn)程立馬退出,并由父進(jìn)程去wait回收,這個(gè)過(guò)程不需要等待,然后父進(jìn)程可以去干其他的活。孫子進(jìn)程因?yàn)樽舆M(jìn)程退出會(huì)成為孤兒進(jìn)程,那它可以由init充當(dāng)父進(jìn)程,并回收。這樣父進(jìn)程和孫子進(jìn)程就可以同時(shí)干活,互不影響,就實(shí)現(xiàn)了多進(jìn)程。
例子2:
#include
#include
#include
#include
int main()
{
pid_t pid;
//創(chuàng)建第一個(gè)子進(jìn)程
pid = fork();
if (pid < 0)
{
perror("fork error:");
exit(1);
}
//第一個(gè)子進(jìn)程
else if (pid == 0)
{
//子進(jìn)程再創(chuàng)建子進(jìn)程
printf("I am the first child process.pid:%d\tppid:%d\n",getpid(),getppid());
pid = fork();
if (pid < 0)
{
perror("fork error:");
exit(1);
}
//第一個(gè)子進(jìn)程退出
else if (pid >0)
{
printf("first procee is exited.\n");
exit(0);
}
//第二個(gè)子進(jìn)程
//睡眠3s保證第一個(gè)子進(jìn)程退出,這樣第二個(gè)子進(jìn)程的父親就是init進(jìn)程里
sleep(3);
printf("I am the second child process.pid: %d\tppid:%d\n",getpid(),getppid());
exit(0);
}
//父進(jìn)程處理第一個(gè)子進(jìn)程退出
if (waitpid(pid, NULL, 0) != pid)
{
perror("waitepid error:");
exit(1);
}
exit(0);
return 0;
}
四.補(bǔ)充測(cè)試程序
1)孤兒進(jìn)程測(cè)試程序
#include
#include
#include
#include
int main()
{
pid_t pid;
//創(chuàng)建一個(gè)進(jìn)程
pid = fork();
//創(chuàng)建失敗
if (pid < 0)
{
perror("fork error:");
exit(1);
}
//子進(jìn)程
if (pid == 0)
{
printf("I am the child process.\n");
//輸出進(jìn)程ID和父進(jìn)程ID
printf("pid: %d\tppid:%d\n",getpid(),getppid());
printf("I will sleep five seconds.\n");
//睡眠5s,保證父進(jìn)程先退出
sleep(5);
printf("pid: %d\tppid:%d\n",getpid(),getppid());
printf("child process is exited.\n");
}
//父進(jìn)程
else
{
printf("I am father process.\n");
//父進(jìn)程睡眠1s,保證子進(jìn)程輸出進(jìn)程id
sleep(1);
printf("father process is exited.\n");
}
return 0;
}
2)僵尸進(jìn)程測(cè)試程序1
int main()
{
pid_t pid;
pid = fork();
if (pid < 0)
{
perror("fork error:");
exit(1);
}
else if (pid == 0)
{
printf("I am child process.I am exiting.\n");
exit(0);
}
printf("I am father process.I will sleep two seconds\n");
//等待子進(jìn)程先退出
sleep(2);
//輸出進(jìn)程信息
system("ps -o pid,ppid,state,command");
printf("father process is exiting.\n");
return 0;
}
3)僵尸進(jìn)程測(cè)試程序2
#include
#include
#include
#include
int main()
{
pid_t pid;
//循環(huán)創(chuàng)建子進(jìn)程
while(1)
{
pid = fork();
if (pid < 0)
{
perror("fork error:");
exit(1);
}
else if (pid == 0)
{
printf("I am a child process.\nI am exiting.\n");
//子進(jìn)程退出,成為僵尸進(jìn)程
exit(0);
}
else
{
//父進(jìn)程休眠20s繼續(xù)創(chuàng)建子進(jìn)程
sleep(20);
continue;
}
}
return 0;
}
4)僵尸進(jìn)程測(cè)試程序2--測(cè)試效果
運(yùn)行可執(zhí)行程序顯示:
I am a child process.
I am exiting.
I am a child process.
I am exiting.
I am a child process.
I am exiting.
I am a child process.
I am exiting.
I am a child process.
I am exiting.
I am a child process.
I am exiting.
Killed
開(kāi)另外一個(gè)終端:
運(yùn)行:
ps -a -o pid,ppid,state,cmd
顯示:(狀態(tài)Z代表僵尸進(jìn)程)
S PID PPID CMD
S 3213 2529 ./pid1
Z 3214 3213 [pid1]
Z 3215 3213 [pid1]
Z 3219 3213 [pid1]
Z 3220 3213 [pid1]
Z 3221 3213 [pid1]
R 3223 3104 ps -a -o state,pid,ppid,cmd
用第一種方法,解決僵尸進(jìn)程,殺死其父進(jìn)程
運(yùn)行:kill -9 3213
注意:僵尸進(jìn)程無(wú)法用kill直接殺死,如kill -9 3214,再用上面命令去查看進(jìn)程狀態(tài),發(fā)現(xiàn)3214進(jìn)程還在。
五. 參考文獻(xiàn)
//www.cnblogs.com/Anker/p/3271773.html
《unix環(huán)境高級(jí)編程》第八章