一 : linux僵死进程的避免
在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 <stdio.h>
#include<sys/types.h>
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信号。子进程死后,会发送SIGCHLD信号给父进程,父进程收到此信号后,履行 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进程。在另外一些体系中,它干脆拒绝成为任何defunct进程的父进程,而是立刻消灭它们。假如你运用上述体系的一种,可以写一个简单的循环,用属于init的defunct进程填满进程表。这大约不会令你的体系管理员很愉快吧?
你的义务:肯定你的父进程不要疏忽 SIGCLD,也不要 wait() 它 fork() 的一切进程。不过,你也未必 要总是这样做(比方,你要起一个 daemon 或是别的什么货色),然而你必需警惕编程,假如你是一个 fork()的老手。另外,也不要在心理上有任何约束。
总结:
子进程成为 defunct 直到父进程 wait(),除非父进程疏忽了 SIGCLD 。
更进一步,父进程没有 wait()
就灭亡(仍假如父进程没有疏忽 SIGCLD )的子进程(运动的或许 defunct)成为 init 的子进程,init 用重手段处理它们。
二 : Linux 系统中僵尸进程
Linux 系统中僵尸进程和现实中僵尸(虽然我也没见过)类似,虽然已经死了,但是由于没人给它们收尸,还能四处走动。(www.61k.com]僵尸进程指的是那些虽然已经终止的进程,但仍然保留一些信息,等待其父进程为其收尸。配图源自 Flickr
僵尸进程如何产生的?
如果一个进程在其终止的时候,自己就回收所有分配给它的资源,系统就不会产生所谓的僵尸进程了。那么我们说一个进程终止之后,还保留哪些信息?为什么终止之后还需要保留这些信息呢?
一个进程终止的方法很多,进程终止后有些信息对于父进程和内核还是很有用的,例如进程的ID号、进程的退出状态、进程运行的CPU时间等。因此进程在终止时,回收所有内核分配给它的内存、关闭它打开的所有文件等等,但是还会保留以上极少的信息,以供父进程使用。父进程可以使用 wait/waitpid 等系统调用来为子进程收拾,做一些收尾工作。
因此,一个僵尸进程产生的过程是:父进程调用fork创建子进程后,子进程运行直至其终止,它立即从内存中移除,但进程描述符仍然保留在内存中(进程描述符占有极少的内存空间)。子进程的状态变成EXIT_ZOMBIE
,并且向父进程发送SIGCHLD 信号,父进程此时应该调用 wait()
系统调用来获取子进程的退出状态以及其它的信息。在 wait 调用之后,僵尸进程就完全从内存中移除。因此一个僵尸存在于其终止到父进程调用 wait 等函数这个时间的间隙,一般很快就消失,但如果编程不合理,父进程从不调用 wait 等系统调用来收集僵尸进程,那么这些进程会一直存在内存中。
在 Linux 下,我们可以使用 ps 等命令查看系统中僵尸进程,僵尸进程的状态标记为‘Z’:
产生一个僵尸进程
根据上面的描述,我们很容易去写一个程序来产生僵尸进程,如下代码:
#include <stdio.h> #include <sys/types.h> int main() { //fork a child process pid_t pid = fork(); if (pid > 0) //parent process { printf("in parent process, sleep for one miniute...zZ...\n"); sleep(60); printf("after sleeping, and exit!\n"); } else if (pid == 0) { //child process exit, and to be a zombie process printf("in child process, and exit!\n"); exit(0); } return 0; }
父进程并没有写 wait 等系统调用函数,因此在子进程退出之后变成僵尸进程,父进程并没有为其去收尸。我们使用下面命令编译运行该进程,然后查看系统中进程状态:
guohailin@guohailin:~/Documents$ gcc zombie.c -o zombie guohailin@guohailin:~/Documents$ ./zombie in parent process, sleep for one miniute...zZ... in child process, and exit! # 打开另一个终端: guohailin@guohailin:~$ ps aux | grep -w 'Z' 1000 2211 1.2 0.0 0 0 ? Z 13:24 6:53 [chromium-browse] <defunct> 1000 4400 0.0 0.0 0 0 ? Z 10月16 0:00 [fcitx] <defunct> 1000 10871 0.0 0.0 0 0 pts/4 Z+ 22:32 0:00 [zombie] <defunct>
从上面可以看出,系统中多了一个僵尸进程。但如果等父进程睡眠醒来退出之后,我们再次查看系统进程信息,发现刚才的僵尸进程不见了。
guohailin@guohailin:~/Documents$ ./zombie in parent process, sleep for one miniute...zZ... in child process, and exit! after sleeping, and exit! guohailin@guohailin:~/Documents$ ps aux | grep -w 'Z' 1000 2211 1.2 0.0 0 0 ? Z 13:24 6:53 [chromium-browse] <defunct> 1000 4400 0.0 0.0 0 0 ? Z 10月16 0:00 [fcitx] <defunct>
这是为什么呢?父进程到死都也没有为其子进程收尸呀,怎么父进程退出之后,那个僵尸进程就消失了呢?难道父进程在退出时会为子进程收拾吗?其实不然....真正的原因是:父进程死掉之后,其所有子进程过继给 init 进程,init 进程成为该僵尸进程的新进程,init 进程会周期性地去调用 wait 系统调用来清除它的僵尸孩子。因此,你会发现上面例子中父进程死掉之后,僵尸进程也跟着消失,其实是 init 进程为其收尸的!
怎样避免僵尸进程的产生
不能使用 kill 后接 SIGKILL
信号这样的命令像杀死普通进程一样杀死僵尸进程,因为僵尸进程是已经死掉的进程,它不能再接收任何信号。事实上,如果系统中僵尸进程并不多的话,我们也无需去消除它们,少数的僵尸进程并不会对系统的性能有什么影响。
那么在编程时,如果能避免系统中大量产生僵尸进程呢?根据上面描述的,子进程在终止时会向父进程发 SIGCHLD 信号,Linux 默认是忽略该信号的,我们可以显示安装该信号,在信号处理函数中调用 wait 等函数来为其收尸,这样就能避免僵尸进程长期存在于系统中了。示例代码如下:
#include <stdio.h> #include <signal.h> #include <string.h> #include <sys/types.h> #include <sys/wait.h> sig_atomic_t child_exit_status; void clean_up_child_process(int signal_num) { /* clean up child process */ int status; wait (&status); /* store its exit status in a global variable */ child_exit_status = status; } int main() { /* handle SIGCHLD by calling clean_up_child_process */ struct sigaction sigchild_action; memset(&sigchild_action, 0, sizeof(sigchild_action)); sigchild_action.sa_handler = &clean_up_child_process; sigaction(SIGCHLD, &sigchild_action, NULL); /* fork a child, and let the child process dies before parent */ pid_t c_pid; c_pid = fork(); if (c_pid > 0) { printf("in parent process, and sleep for on mininute...zZ...\n"); sleep(60); } else if(c_pid == 0) { printf("in child process, and exit now\n"); exit(0); } else { printf("fork failed!\n"); } return 0; }扩展:linux 僵尸进程 / linux查看僵尸进程 / linux杀死僵尸进程
参考资料
扩展:linux 僵尸进程 / linux查看僵尸进程 / linux杀死僵尸进程
三 : linux 如何清理僵尸进程
今天在维护服务器的时候,发现有5个nova-novncproxy的僵尸进程。[www.61k.com]
26327 ? S 0:05 \_ /usr/bin/python /usr/bin/nova-novncproxy --config-file=/etc/nova/nova.conf 4765 ? Z 0:00 \_ [nova-novncproxy] <defunct> 4766 ? Z 0:00 \_ [nova-novncproxy] <defunct> 4767 ? Z 0:00 \_ [nova-novncproxy] <defunct> 4768 ? Z 0:00 \_ [nova-novncproxy] <defunct> 4769 ? Z 0:00 \_ [nova-novncproxy] <defunct>
之前对于僵尸进程的了解并不深,赶紧找了篇相关文章来学习一下,该如何处理。
定义
In UNIX System terminology, a process that has terminated,but whose parent has not yet waited for it, is called a zombie.
在UNIX 系统中,一个进程结束了,但是他的父进程没有等待(调用wait / waitpid)他, 那么他将变成一个僵尸进程. 在fork()/execve()过程中,假设子进程结束时父进程仍存在,而父进程fork()之前既没安装SIGCHLD信号处理函数调用 waitpid()等待子进程结束,又没有显式忽略该信号,则子进程成为僵尸进程。
如何查看linux系统上的僵尸进程,如何统计有多少僵尸进程?
#ps -ef | grep defunct
或者查找状态为Z的进程,Z就是代表zombie process,僵尸进程的意思。
另外使用top命令查看时有一栏为S,如果状态为Z说明它就是僵尸进程。
Tasks: 95 total, 1 running, 94 sleeping, 0 stopped, 0 zombie
top命令中也统计了僵尸进程。或者使用下面的命令:
ps -ef | grep defunct | grep -v grep | wc -l
如何杀死僵尸进程呢?
一般僵尸进程很难直接kill掉,不过您可以kill僵尸爸爸。父进程死后,僵尸进程成为”孤儿进程”,过继给1号进程init,init始终会负责清理僵尸进程.它产生的所有僵尸进程也跟着消失。
ps -e -o ppid,stat | grep Z | cut -d” ” -f2 | xargs kill -9
或
kill -HUP `ps -A -ostat,ppid | grep -e ’^[Zz]‘ | awk ’{print $2}’`
当然您可以自己编写更好的shell脚本,欢迎与大家分享。
我将nova-novncproxy stop后再start,僵尸进程即消失,问题解决。
另外子进程死后,会发送SIGCHLD信号给父进程,父进程收到此信号后,执行waitpid()函数为子进程收尸。就是基于这样的原理:就算父进程没有调用wait,内核也会向它发送SIGCHLD消息,而此时,尽管对它的默认处理是忽略,如果想响应这个消息,可以设置一个处理函数。
如何避免僵尸进程呢?
处理SIGCHLD信号并不是必须的。但对于某些进程,特别是服务器进程往往在请求到来时生成子进程处理请求。如果父进程不等待子进程结束,子进程将成为僵尸进程(zombie)从而占用系统资源。如果父进程等待子进程结束,将增加父进程的负担,影响服务器进程的并发性能。在Linux下 可以简单地将 SIGCHLD信号的操作设为SIG_IGN。
signal(SIGCHLD,SIG_IGN);
这样,内核在子进程结束时不会产生僵尸进程。这一点与BSD4不同,BSD4下必须显式等待子进程结束才能释放僵尸进程
或者
用两次fork(),而且使紧跟的子进程直接退出,是的孙子进程成为孤儿进程,从而init进程将负责清除这个孤儿进程。
本文标题:linux僵死进程-linux僵死进程的避免61阅读| 精彩专题| 最新文章| 热门文章| 苏ICP备13036349号-1