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进程。在另外⼀些系统