如何防止僵尸进程?

如何防止僵尸进程?1 僵尸进程 僵尸进程是当子进程比父进程先结束 而父进程又没有回收子进程 释放子进程占用的资源 此时子进程将成为一个僵尸进程 如果父进程先退出 子进程被 init 接管 子进程退出后 init 会回收其占用的相关资源 2 产生原因 a 子进程结束后向父进程发出 SIGCHLD 信号 父进程默认忽略了它 b 父进程没有调用 wait 或 waitpid 函数来等待子进程的结束 c 网络原

1.僵尸进程:僵尸进程是当子进程比父进程先结束,而父进程又没有回收子进程,释放子进程占用的资源,此时子进程将成为一个僵尸进程。如果父进程先退出 ,子进程被init接管,子进程退出后init会回收其占用的相关资源。

2.产生原因:

a. 子进程结束后向父进程发出SIGCHLD信号,父进程默认忽略了它;

b. 父进程没有调用wait()或waitpid()函数来等待子进程的结束;

c. 网络原因有时会引起僵尸进程;

3. 危害

僵尸进程会占用系统资源,如果很多,则会严重影响服务器的性能;

4.如何防止僵尸进程

(1) 让僵尸进程成为孤儿进程,由init进程回收;(手动杀死父进程)

(2) 调用fork()两次;

(3) 捕捉SIGCHLD信号,并在信号处理函数中调用wait函数;

多进程编程之如何避免僵尸进程

1、两次fork()来避免僵尸进程      

   当我们只fork()一次后,存在父进程和子进程。这时有两种方法来避免产生僵尸进程:

    1)父进程调用waitpid()等函数来接收子进程退出状态;

    2)父进程先结束,子进程则自动托管到Init进程(pid = 1)。 

  考虑子进程先于父进程结束的情况:     

  • 若父进程未处理子进程退出状态,在父进程退出前,子进程一直处于僵尸进程状态。
  • 若父进程调用waitpid()(这里使用阻塞调用确保子进程先于父进程结束)来等待子进程结束,将会使父进程在调用waitpid()后进入睡眠状态,只有子进程结束父进程的waitpid()才会返回。 如果存在子进程结束,但父进程还未执行到waitpid()的情况,那么这段时期子进程也将处于僵尸进程状态。

      由此,可以看出父进程与子进程有父子关系,除非保证父进程先于子进程结束或者保证父进程在子进程结束前执行waitpid(),子进程均有机会成为僵尸进程。那么如何使父进程更方便地创建不会成为僵尸进程的子进程呢?这就要用两次fork()了。

      父进程一次fork()后产生一个子进程随后立即执行waitpid(子进程pid, NULL, 0)来等待子进程结束,然后子进程fork()后产生孙子进程随后立即exit(0)。这样子进程顺利终止(父进程仅仅给子进程收尸,并不需要子进程的返回值),然后父进程继续执行。这时的孙子进程由于失去了它的父进程(即是父进程的子进程),将被转交给Init进程托管。于是父进程与孙子进程无继承关系了,它们的父进程均为Init,Init进程在其子进程结束时会自动收尸,这样也就不会产生僵尸进程了。

  通用场景:

  一个进程要创建一个进程,两个进程同时处理任务,谁也不耽误谁。如果直接用子进程充当第二个进程的角色,那么问题是这样的:如果父进程处理时间长,子进程处理时间短,那么如果父进程不 wait() 处理的话,子进程就会成为僵尸进程,但如果父进程 wait() 子进程的话,父进程就会阻塞,所有有个方法就是让自己尽快推出,任务让子进程的子进程来处理。

 1 #include <unistd.h> 2 #include <sys/wait.h> 3 #include <stdio.h> 4 #include <stdlib.h> 5 6 int main() 7 { 8 pid_t pid; 9 if( (pid = fork()) < 0 ) 10 { 11 printf("fork error.\n"); 12 exit(-1); 13 } 14 else if(pid == 0) /* first child */ 15 { 16 if( (pid = fork()) < 0 ) 17 { 18 printf("fork error.\n"); 19 exit(-1); 20 } 21 else if(pid > 0) 22 { 23 exit(0); 24 } 25 26 /* We're the second child; our parent becomes init as soon as our real parent exits. */ 27 printf("second child, parent pid = %d\n", getppid()); 28 /* ---------------handle tasks--------------- */ 29 exit(0); 30 } 31 32 if(waitpid(pid, NULL, 0) != pid) /* wait for first child */ 33 { 34 printf("waitpid error.\n"); 35 exit(1); 36 } 37 printf("parent, first child pid = %d\n", pid); 38 /* ---------------handle tasks--------------- */ 39 40 exit(0); 41 }

 

2、通过信号机制来避免僵尸进程

  1)在父进程fork()之前安装SIGCHLD信号处理函数,并在此handler函数中调用waitpid()等待子进程结束,这样,内核才能获得子进程退出信息从而释放那个进程描述符; 

 1 #include <stdio.h> 2 #include <unistd.h> 3 #include <errno.h> 4 #include <stdlib.h> 5 #include <signal.h> 6 7 static void sig_child(int signo); 8 9 int main() 10 { 11 pid_t pid; 12 //创建捕捉子进程退出信号 13 signal(SIGCHLD,sig_child); 14 pid = fork(); 15 if (pid < 0) 16 { 17 perror("fork error:"); 18 exit(1); 19 } 20 else if (pid == 0) 21 { 22 printf("I am child process,pid id %d.I am exiting.\n",getpid()); 23 exit(0); 24 } 25 printf("I am father process.I will sleep two seconds\n"); 26 //等待子进程先退出 27 sleep(2); 28 //输出进程信息 29 system("ps -o pid,ppid,state,tty,command"); 30 printf("father process is exiting.\n"); 31 return 0; 32 } 33 34 static void sig_child(int signo) 35 { 36 pid_t pid; 37 int stat; 38 //处理僵尸进程 39 while ((pid = waitpid(-1, &stat, WNOHANG)) >0) 40 printf("child %d terminated.\n", pid); 41 } 输出结果: (^_^)root@rdenv-100#./a.out  I am father process.I will sleep two seconds I am child process,pid id 9878.I am exiting. child 9878 terminated. PID PPID S TT COMMAND 9764 9762 S pts/2 -zsh 9877 9764 S pts/2 ./a.out 9879 9877 R pts/2 ps -o pid,ppid,state,tty,command father process is exiting.

  2)设置SIGCHLD信号为SIG_IGN(即,忽略SIGHLD信号),系统将不产生僵尸进程。通过signal(SIGCHLD, SIG_IGN)通知内核对子进程的结束不关心,由内核回收。如果不想让父进程挂起,可以在父进程中加入一条语句:signal(SIGCHLD,SIG_IGN);表示父进程忽略SIGCHLD信号,该信号是子进程退出的时候向父进程发送的。常用于并发服务器的性能的一个技巧因为并发服务器常常fork很多子进程,子进程终结之后需要服务器进程去wait清理资源。如果将此信号的处理方式设为忽略,可让内核把僵尸子进程转交给init进程去处理,省去了大量僵尸进程占用系统资源。  

  比如:对于服务器进程,如果父进程不等待子进程就结束,子进程将成为僵尸进程;若父进程等待子进程结束,就会影响服务器进程的并发性能。所以此时一般就将SIGCHLD信号设置为 SIG_IGN.

  注意:当我们在父进程中添加了signal(SIGCHLD,SIG_IGN)时,就不要调用waitpid函数去回收子进程了,否则会报错。man手册中有这样一段话:for waitpid() or waitid()) The process specified by pid (waitpid()) or idtype and id (waitid()) does not exist or is not a child of the calling process. (This can happen for one’s own child if the action for SIGCHLD is set to SIG_IGN. See also the Linux Notes section about threads.

  使用SIG_IGN信号引起的问题:http://blog.csdn.net/taolinke/article/details/

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/231828.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • mongovue mysql_mongoVUE的增删改查操作使用说明(转)「建议收藏」

    mongovue mysql_mongoVUE的增删改查操作使用说明(转)「建议收藏」1、精确查询1)右键点击集合名,再左键点击Find或者直接点击工具栏上的Find2)查询界面,包括四个区域{Find}区,查询条件格式{“sendId”:”000101″}表示查询sendId=000101的记录,(注:MongoDB区分大小写,写成{“sendid”:”000101″}会查询不到)查询条件包含and时,格式为:{“sendId”:”000101″,”operParam5″:”v…

    2022年8月21日
    10
  • 浅谈hosts文件

    浅谈hosts文件原文链接(有图)本文目录:->1.什么是hosts文件?这个文件在哪?->2.hosts文件的作用及用法->Ⅰ加快解析速度查询IP方法①:ping命令方法②:网站查询->Ⅱ屏蔽网站->3.hosts文件打不开怎么办?本文可能需要用到的工具:cmd(windows自带的DOS命令窗口,运行->cmd即可打开,“运…

    2022年7月26日
    9
  • java老版手机游戏合集激活成功教程

    java老版手机游戏合集激活成功教程一、前言最近刚读完一本书:《Netty、Zookeeper、Redis并发实战》,个人觉得Netty部分是写得很不错的,读完之后又对Netty进行了一波很好的复习(之前用springboot+netty+zookeeper模仿dubbo做rpc框架,那时候是刚学netty后自己造的小轮子)。虽然对于Netty的使用已经比较熟悉了,而且还知道它的底层是基于JavaNIO做进一步的封装,使得并发性能和开发效率得到大大的提升。但是,对于同步阻塞、同步非阻塞、异步这

    2022年7月7日
    27
  • spring事务回滚的多种方式「建议收藏」

    spring事务回滚的多种方式「建议收藏」转:https://www.cnblogs.com/zeng1994/p/8257763.htmlstart看下下面的说明,会对理解本人贴出的代码有帮助。1.代码中事务控制的3种方式编程式事务:就是直接在代码里手动开启事务,手动提交,手动回滚。优点就是可以灵活控制,缺点就是太麻烦了,太多重复的代码了。声明式事务:就是使用SpringAop配置事务,这种方式大大的简化了编码。需要注…

    2022年10月19日
    2
  • 2、Java基础02 – 【命令行运行HelloWorld】[通俗易懂]

    2、Java基础02 – 【命令行运行HelloWorld】[通俗易懂]操作步骤:1、新建一个文件夹(可以命名为Java)2、新建一个.txt文本文件,在文件中输入如下代码:publicclassHelloWorld{publicstaticvoidmain(String[]args){System.out.println(“helloworld”);}}3、重命名将文件名改为HelloWorld.java,并创建第一个java源文件4、编译.java文件是java的源文件,但是不能直接运行,必须先被编译成为.class文件才能够

    2022年5月20日
    40
  • python pycharm 调试(debug)快捷键「建议收藏」

    python pycharm 调试(debug)快捷键「建议收藏」1.eclipse配置的debug快捷键1.showexecutionpoint(alt+F10)显示当前所有断点2.stepover(F6)单步调试。若函数A内存在子函数a时,不会进入子函数a内执行单步调试,而是把子函数a当作一个整体,一步执行3.stepinto(F5)单步调试。若函数A内存在子函数a时,会进入子函数a内执行单步调试。4.stepintomycode(Alt+Shift+F7)执行下一行但忽略libraries(导入库的语句)5.force

    2025年6月22日
    2

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号