python 多进程 避免僵尸进程,

  python 多进程 避免僵尸进程,

  1.前言之前在《unix环境高级编程》第八章看进程的时候提到了孤儿进程和僵尸进程,这两个概念一直很模糊。今天被问到孤儿进程和僵尸进程是什么,会带来什么问题,如何解决。只停留在概念上,为自己没有深入而感到惭愧。晚上google了一下,又参考了一遍APUE,认真总结了一下,加深了理解。

  2.基本概念我们知道,在unix/linux中,一般情况下,子进程是由父进程创建的,子进程在创建一个新进程。子进程的结束和父进程的运行是异步过程,即父进程永远无法预测子进程何时结束。当一个进程完成其工作终止时,其父进程需要调用wait()或waitpid()系统调用来获取子进程的终止状态。

  孤立进程:如果一个父进程退出,而它的一个或多个子进程仍在运行,这些子进程将成为孤立进程。孤儿将被init进程(进程号1)收养,init进程将收集它们的状态。

  僵尸:一个进程使用fork创建一个子进程。如果子进程退出,而父进程没有调用wait或waitpid来获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这个过程被称为僵尸过程。

  3.问题和危害unix提供了一种机制,保证只要父进程在最后想知道子进程的状态信息,就能得到。这个机制是:当每个进程退出时,内核释放该进程的所有资源,包括打开的文件、占用的内存等。但是仍然为它保留了一些信息(包括进程号、进程ID、退出状态、进程的终止状态、运行时间、进程占用的CPU时间等。).直到父进程通过wait/waitpid得到它,它才被释放。但这导致了问题。如果进程不调用wait/waitpid,预留的信息不会被释放,它的进程号会一直被占用。但是,系统可以使用的进程号的数量是有限的。如果生成了大量死进程,系统将无法生成新进程,因为没有可用的进程号。这就是僵尸进程的危害,应该避免。

  孤儿进程是没有父进程的进程,所以孤儿进程就落在了init进程身上,init进程就像一个民政局,负责处理孤儿进程的善后事宜。每当出现一个孤儿进程,内核就将孤儿进程的父进程设置为init,init进程会循环等待()其已经退出的子进程。这样,当一个孤儿进程悲惨地结束它的生命周期时,init进程将代表党和政府处理它的所有善后事宜。因此,孤儿进程不会造成任何伤害。

  任何子进程(除了init)在exit()之后都不会立即消失,而是留下一个数据结构叫僵尸进程,等待父进程处理。这是每个子流程最后都要经历的阶段。如果子进程在exit()后面,父进程没时间处理,那么用ps命令可以看到子进程的状态是“Z”。如果父进程能及时处理,用ps命令看到子进程的僵尸状态可能就来不及了,但这并不代表子进程不经历僵尸状态。如果父进程在子进程结束前退出,子进程将由init接管。Init将把处于僵死状态的子进程作为父进程来处理。

  僵尸危险场景:

  例如,有一个进程会定期产生一个子进程。这个子进程需要做的事情很少,做完该做的事情就退出了。所以这个子进程的生命周期很短。而父进程只生成新的子进程,至于子进程退出后会发生什么,它视而不见。这样,系统运行一段时间后,系统中就会出现很多死进程。如果你用ps命令检查它们,你会严格地说,僵尸进程并不是问题的根源,罪魁祸首是产生大量僵尸进程的父进程。所以,当我们寻求如何消灭系统中的大量僵尸进程时,答案就是射杀产生大量僵尸进程的元凶(即通过KILL发送SIGTERM或SIGKILL信号)。罪魁祸首进程被枪毙后,其产生的僵尸进程就成了孤儿进程。这些孤儿进程会被init进程接管,init进程会等待()这些孤儿进程,释放它们所占用的系统进程表中的资源。这样,这些僵尸孤儿进程就可以留下来了。

  4.孤儿进程和僵尸进程测试孤儿进程测试程序如下:

  #包含stdio.h

  #包含stdlib.h

  #包含错误号h

  #包括unistd.h

  int main()

  {

  pid _ t pid

  //创建一个进程

  PID=fork();

  //创建失败。

  如果(pid 0)

  {

  perror(fork错误:);

  出口(1);

  }

  //子进程

  如果(pid==0)

  {

  printf(我是子进程。\ n’);

  //输出进程ID和父进程ID

  printf(pid: %d\tppid:%d\n ,getpid(),getppid());

  printf(‘我会睡五秒钟。\ n’);

  //睡眠5s,保证父进程先退出。

  睡眠(5);

  printf(pid: %d\tppid:%d\n ,getpid(),getppid());

  printf(子进程退出。\ n’);

  }

  //父进程

  其他

  {

  printf(我是父进程。\ n’);

  //父进程休眠1s,保证子进程输出进程id。

  睡眠(1);

  printf(父进程退出。\ n’);

  }

  返回0;

  }测试结果如下:

  僵尸测试程序如下:

  #包含stdio.h

  #包括unistd.h

  #包含错误号h

  #包含stdlib.h

  int main()

  {

  pid _ t pid

  PID=fork();

  如果(pid 0)

  {

  perror(fork错误:);

  出口(1);

  }

  else if (pid==0)

  {

  printf(我是子进程。我退出。\ n’);

  退出(0);

  }

  printf(我是父进程。我就睡两秒\ n’);

  //等待子进程先退出

  睡眠(2);

  //输出进程信息

  system(ps -o pid,ppid,state,tty,command );

  printf(父进程正在退出。\ n’);

  返回0;

  }测试结果如下:

  僵尸测试二:父进程循环创建子进程,子进程退出,产生多个僵尸进程。该过程如下:

  #包含stdio.h

  #包含stdlib.h

  #包括unistd.h

  #包含错误号h

  int main()

  {

  pid _ t pid

  //在循环中创建子进程

  while(1)

  {

  PID=fork();

  如果(pid 0)

  {

  perror(fork错误:);

  出口(1);

  }

  else if (pid==0)

  {

  printf(我是子进程。\ n我要退出。\ n’);

  //子进程退出,成为僵尸进程。

  退出(0);

  }

  其他

  {

  //父进程休眠20秒,继续创建子进程。

  睡眠(20);

  继续;

  }

  }

  返回0;

  }程序测试结果如下:

  5.僵尸进程的解决方案(1)通过信号机制在子进程退出时向父进程发送SIGCHILD信号,父进程处理SIGCHILD信号。在信号处理函数中调用wait来处理僵尸进程。测试程序如下:

  #包含stdio.h

  #包括unistd.h

  #包含错误号h

  #包含stdlib.h

  #包含信号. h

  静态void SIG _ child(int signo);

  int main()

  {

  pid _ t pid

  //创建捕获子进程退出信号。

  signal(SIGCHLD,SIG _ child);

  PID=fork();

  如果(pid 0)

  {

  perror(fork错误:);

  出口(1);

  }

  else if (pid==0)

  {

  printf(我是子进程,pid id %d,我正在退出。\n ,getpid());

  退出(0);

  }

  printf(我是父进程。我就睡两秒\ n’);

  //等待子进程先退出

  睡眠(2);

  //输出进程信息

  system(ps -o pid,ppid,state,tty,command );

  printf(父进程正在退出。\ n’);

  返回0;

  }

  静态void sig_child(int signo)

  {

  pid _ t pid

  int stat

  //处理僵尸进程

  while ((pid=waitpid(-1,stat,WNOHANG)) 0)

  printf(子级%d已终止。\n ,PID);

  }测试结果如下:

  (2)叉两下

  055-79000第8.6节非常详细。原理是让子进程成为孤儿进程,让它的父进程成为init进程,通过它可以处理僵尸进程。测试程序如下:

  #包含stdio.h

  #包含stdlib.h

  #包括unistd.h

  #包含错误号h

  int main()

  {

  pid _ t pid

  //创建第一个子进程

  PID=fork();

  如果(pid 0)

  {

  perror(fork错误:);

  出口(1);

  }

  //第一个子进程

  else if (pid==0)

  {

  //子进程重新创建子进程

  printf(我是第一个子进程. pid:%d\tppid:%d\n ,getpid(),getppid());

  PID=fork();

  如果(pid 0)

  {

  perror(fork错误:);

  出口(1);

  }

  //第一个子进程退出

  else if (pid 0)

  {

  printf(第一个进程退出。\ n’);

  退出(0);

  }

  //第二个子进程

  //休眠3s,确保第一个子进程退出,这样第二个子进程的父进程就在init进程中了。

  睡眠(3);

  printf(我是第二个子进程. pid: %d\tppid:%d\n ,getpid(),getppid());

  退出(0);

  }

  //父进程处理第一个子进程的退出

  if (waitpid(pid,NULL,0)!=pid)

  {

  perror(waitepid错误:);

  出口(1);

  }

  退出(0);

  返回0;

  }测试结果如下图所示:

  6.参考《Unix 环境高级编程》,第8章

  http://www.rosoo.net/a/201109/15071.html

  http://blog.chinaunix.net/uid-1829236-id-3166986.html

  http://forkhope.diandian.com/post/2012-10-01/40040574200

  涉及

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: