Linux僵⼫进程产⽣及如何避免以及daemon进程
在fork()/execve()过程中,假设⼦进程结束时⽗进程仍存在,⽽⽗进程fork()之前既没安装SIGCHLD信号处理函数调⽤waitpid()等待⼦进程结束,⼜没有显式忽略该信号,则⼦进程成为僵⼫进程,⽆法正常结束,此时即使是root⾝份kill-9也不能杀死僵⼫进程。补救办法是杀死僵⼫进程的⽗进程(僵⼫进程的⽗进程必然存在),僵⼫进程成为"孤⼉进程",过继给1号进程init,init始终会负责清理僵⼫进程。
僵⼫进程是指的⽗进程已经退出,⽽该进程dead之后没有进程接受,就成为僵⼫进程.(zombie)进程 怎样产⽣僵⼫进程的:
⼀个进程在调⽤exit命令结束⾃⼰的⽣命的时候,其实它并没有真正的被销毁,⽽是留下⼀个称
为僵⼫进程(Zombie)的数据结构(系统调⽤exit,它的作⽤是使进程退出,但也仅仅限于将⼀个正常的进程变成⼀个僵⼫进程,并不能将其完全销毁)。在Linux进程的状态中,僵⼫进程
是⾮常特殊的⼀种,它已经放弃了⼏乎所有内存空间,没有任何可执⾏代码,也不能被调度,仅仅在进程列表中保留⼀个位置,记载该进程的退
出状态等信息供其他进程收集,除此之外,僵⼫进程不再占有任何内存空间。它需要它的⽗进程来为它收⼫,如果他的⽗进程没安装SIGCHLD信
号处理函数调⽤wait或waitpid()等待⼦进程结束,⼜没有显式忽略该信号,那么它就⼀直保持僵⼫状态,如果这时⽗进程结束了,那么init进程⾃动
会接⼿这个⼦进程,为它收⼫,它还是能被清除的。但是如果如果⽗进程是⼀个循环,不会结束,那么⼦进程就会⼀直保持僵⼫状态,这就是为什么系统中有时会有很多的僵⼫进程。
Linux系统对运⾏的进程数量有限制,如果产⽣过多的僵⼫进程占⽤了可⽤的进程号,将会导致
新的进程⽆法⽣成。这就是僵⼫进程对系统的最⼤危害。
僵⼫进程实例:
/*-----zombie1.c-----*/
#include "sys/types.h"
#include "sys/wait.h"
#include "stdio.h"
#include "unistd.h"
int main(int argc, char* argv[])
{
while(1)
pid_t chi = fork();
if(chi == 0)
{
execl("/bin/bash","bash","-c","ls",NULL); }
sleep(2);脚本发生错误怎么办
}
会不停地产⽣僵死进程ls;
/*-----zombie2.c-----*/
#include
#include
main()
{
if(!fork())
{
printf("child pid=%d\n", getpid());
exit(0);
}
/*wait();*/
/*waitpid(-1,NULL,0);*/
sleep(60);
printf("parent pid=%d \n", getpid());
exit(0);
60s内会不断产⽣僵⼫进程,知道⽗进程exit(0);
如果在调⽤wait/waitpid来为⼦进程收⼫,就不会产⽣僵⼫进程了。
PS:运⾏例⼦,先gcc zombie1.c -o zombie编译,然后运⾏zombie;
然后可以可⽤ps -ef来查看是否产⽣了僵⼫进程。
怎么查看僵⼫进程:
利⽤命令ps,可以看到有标记为Z的进程就是僵⼫进程。
怎样来清除僵⼫进程:
1.改写⽗进程,在⼦进程死后要为它收⼫。具体做法是接管SIGCHLD信号。⼦进程死后,会发送SIGC
HLD信号给⽗进程,⽗进程收到此信号后,执⾏ waitpid()函数为⼦进程收⼫。这是基于这样的原理:就算⽗进程没有调⽤wait,内核也会向它发送SIGCHLD消息,尽管对的默认处理是忽略,如果想响应这个消息,可以设置⼀个处理函数。
2.把⽗进程杀掉。⽗进程死后,僵⼫进程成为"孤⼉进程",过继给1号进程init,init始终会负责清理僵⼫进程.它产⽣的所有僵⼫进程也跟着消失。
在Linux中可以⽤
ps auwx
发现僵⼫进程
a all w/ tty, including other users 所有窗⼝和终端,包括其他⽤户的进程
u user-oriented ⾯向⽤户(⽤户友好)
-w,w wide output 宽格式输出
x processes w/o controlling ttys
在僵⼫进程后⾯会标注
ps axf
看进程树,以树形⽅式现实进程列表
ps axm
会把线程列出来,在linux下进程和线程是统⼀的,是轻量级进程的两种⽅式。
ps axu
显⽰进程的详细状态
===========================================
killall
kill -15
kill -9
⼀般都不能杀掉 defunct进程
⽤了kill -15,kill -9以后之后反⽽会多出更多的僵⼫进程
kill -kill pid
fuser -k pid
可以考虑杀死他的parent process,
kill -9 他的parent process
=========================================== ⼀个已经终⽌,但是其⽗进程尚未对其进⾏善后处理(获取终⽌⼦进程的有关信息、释放它仍占⽤的资源)的进程被称为僵死进程(Zombie Process)。
避免zombie的⽅法:
1)在SVR4中,如果调⽤signal或sigset将SIGCHLD的配置设置为忽略,则不会产⽣僵死⼦进程。另外,使⽤SVR4版的 sigaction,则可设置SA_NOCLDWAIT标志以避免⼦进程僵死。
Linux中也可使⽤这个,在⼀个程序的开始调⽤这个函数
signal(SIGCHLD,SIG_IGN);
2)调⽤fork两次。程序8 - 5 实现了这⼀点。
3)⽤waitpid等待⼦进程返回.
===========================================
zombie进程是僵死进程。防⽌它的办法,⼀是⽤wait,waitpid之类的函数获得
进程的终⽌状态,以释放资源。另⼀个是fork两次
===========================================
defunct进程只是在process table⾥还有⼀个记录,其他的资源没有占⽤,除⾮你的系统的process个数的限制已经快超过了,zombie进程不会有更多的坏处。
可能唯⼀的⽅法就是reboot系统可以消除zombie进程。
===========================================
任何程序都有僵⼫状态,它占⽤⼀点内存资源(也就是进程表⾥还有⼀个记录),仅仅是表象⽽已不必害怕。如果程序有问题有机会遇见,解决⼤批量僵⼫简单有效的办法是重起。kill是⽆任何效果的
fork与zombie/defunct"
在Unix下的⼀些进程的运作⽅式。当⼀个进程死亡时,它并不是完全的消失了。进程终⽌,它不再运⾏,但是还有⼀些残留的⼩东西等待⽗进程收回。这些残留的东西包括⼦进程的返回值和其他的⼀些东西。当⽗进程 fork()⼀个⼦进程后,它必须⽤ wait() 或者 waitpid() 等待⼦进程退出。正是这个wait() 动作来让⼦进程的残留物消失。
⾃然的,在上述规则之外有个例外:⽗进程可以忽略 SIGCLD 软中断⽽不必要 wait()。可以这样做到(在⽀持它的系统上,⽐如Linux):
main()
{
signal(SIGCLD, SIG_IGN); /* now I don't have to wait()! */
.
.
fork();
fork();
fork(); /* Rabbits, rabbits, rabbits! */
}
现在,⼦进程死亡时⽗进程没有 wait(),通常⽤ ps 可以看到它被显⽰为“”。它将永远保持这样直到⽗进程 wait(),或者按以下⽅法处理。
这⾥是你必须知道的另⼀个规则:当⽗进程在它wait()⼦进程之前死亡了(假定它没有忽略SIGCLD),⼦进程将把 init(pid1)进程作为它的⽗进程。如果⼦进程⼯作得很好并能够控制,这并不是问题。但如果⼦进程已经是defunct,我们就有了⼀点⼩⿇烦。看,原先的⽗进程不可能再 wait(),因为它已经消亡了。这样,init 怎么知道 wait() 这些zombie 进程。
答案:不可预料的。在⼀些系统上,init周期性的破坏掉它所有的defunct进程。在另外⼀些系统
发布评论