61阅读

linux僵死进程-linux僵死进程的避免

发布时间:2018-04-22 所属栏目:怎么杀死僵尸进程

一 : 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 
linux僵死进程 Linux 系统中僵尸进程

僵尸进程如何产生的?

如果一个进程在其终止的时候,自己就回收所有分配给它的资源,系统就不会产生所谓的僵尸进程了。那么我们说一个进程终止之后,还保留哪些信息?为什么终止之后还需要保留这些信息呢?

一个进程终止的方法很多,进程终止后有些信息对于父进程和内核还是很有用的,例如进程的ID号、进程的退出状态、进程运行的CPU时间等。因此进程在终止时,回收所有内核分配给它的内存、关闭它打开的所有文件等等,但是还会保留以上极少的信息,以供父进程使用。父进程可以使用 wait/waitpid 等系统调用来为子进程收拾,做一些收尾工作。

因此,一个僵尸进程产生的过程是:父进程调用fork创建子进程后,子进程运行直至其终止,它立即从内存中移除,但进程描述符仍然保留在内存中(进程描述符占有极少的内存空间)。子进程的状态变成EXIT_ZOMBIE,并且向父进程发送SIGCHLD 信号,父进程此时应该调用 wait() 系统调用来获取子进程的退出状态以及其它的信息。在 wait 调用之后,僵尸进程就完全从内存中移除。因此一个僵尸存在于其终止到父进程调用 wait 等函数这个时间的间隙,一般很快就消失,但如果编程不合理,父进程从不调用 wait 等系统调用来收集僵尸进程,那么这些进程会一直存在内存中。

在 Linux 下,我们可以使用 ps 等命令查看系统中僵尸进程,僵尸进程的状态标记为‘Z’:
linux僵死进程 Linux 系统中僵尸进程

产生一个僵尸进程

根据上面的描述,我们很容易去写一个程序来产生僵尸进程,如下代码:

#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杀死僵尸进程

参考资料

  • http://www.howtogeek.com/119815/

扩展: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僵死进程的避免
本文地址: http://www.61k.com/1157575.html

61阅读| 精彩专题| 最新文章| 热门文章| 苏ICP备13036349号-1