如何防止僵尸进程?

如何防止僵尸进程?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)
上一篇 2025年11月18日 下午7:01
下一篇 2025年11月18日 下午7:22


相关推荐

  • 编译命令行终端 swift

    编译命令行终端 swift

    2022年1月13日
    46
  • 最近在使用腾讯元宝的时候,Deepseek突然变得卡壳了!

    最近在使用腾讯元宝的时候,Deepseek突然变得卡壳了!

    2026年3月13日
    1
  • 前端缓存机制

    前端缓存机制目录前言目的缓存过程分析强制缓存 强缓存 强制缓存的缓存规则浏览器的缓存存放在哪里协商缓存更新缓存合理应用缓存前言对于浏览器缓存 每个前端开发者应该都不会陌生 同时它也是我们在日常开发中存在的一个非常重要的优化手段 无论在节省带宽 提高加载和渲染速度 减少网络阻塞 以及提高用户体验上 都发挥着很重要的作用 前端缓存主要是分为 HTTP 缓存和浏览器缓存 其中 HTTP 缓存是在 HTTP 请求传输时用到的缓存 主要在服务器代码上设置 而浏览器缓存则主要由前端开发在

    2026年3月19日
    1
  • 矩阵行列式计算

    矩阵行列式计算矩阵行列式计算要求矩阵行列式 需要通过初等变换使得矩阵变为三角矩阵 然后对角线元素之积就是矩阵行列式的值 但是一般的初等变换可能导致浮点数的产生从而影响精度 因此这里使用辗转相除法进行初等变换 本算法是将其转化为上三角矩阵 因此从第一行开始 处理第 ii 行时 要将第 i 1 i 1 行到第 nn 行的第 ii 列的元素转化为 00 这样处理完成后就是上三角矩阵了 对于第 ii 行 处理第

    2026年3月16日
    1
  • 新书上市 | 世界名校数据挖掘经典《斯坦福数据挖掘教程(第3版)》「建议收藏」

    新书上市 | 世界名校数据挖掘经典《斯坦福数据挖掘教程(第3版)》「建议收藏」题图|作者为ScottUllman《斯坦福数据挖掘教程(第3版)》上架之后,这是我们第一次整篇文章介绍这本书。这本书相当受欢迎(前两个版本累计销量超过5万册),尤其是受学校青…

    2022年6月26日
    51
  • Java线程池Executor详解

    Java线程池Executor详解线程池类图我们最常使用的 Executors 实现创建线程池使用线程主要是用上述类图中提供的类 在上边的类图中 包含了一个 Executor 框架 它是一个根据一组执行策略的调用调度执行和控制异步任务的框架 目的是提供一种将任务提交与任务如何运行分离开的机制 它包含了三个 executor 接口 Executor 运行新任务的简单接口 ExecutorServ 扩展了 Executor 添加了用来管理执行器生命周期和任务生命周期的方法 ScheduleExcu 扩展了 ExecutorSer

    2026年3月18日
    2

发表回复

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

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