linux下的僵尸进程处理SIGCHLD信号

news2024/10/9 16:26:37

阅读目录

  • 对于waitpid的p i d参数的解释与其值有关:
  • wait与waitpid区别:

转自:linux下的僵尸进程处理SIGCHLD信号 - Jessica程序猿 - 博客园

什么是僵尸进程

首先内核会释放终止进程(调用了exit系统调用)所使用的所有存储区,关闭所有打开的文件等,但内核为每一个终止子进程保存了一定量的信息。这些信息至少包括进程ID,进程的终止状态,以及该进程使用的CPU时间,所以当终止子进程的父进程调用wait或waitpid时就可以得到这些信息。

而僵尸进程就是指:一个进程执行了exit系统调用退出,而其父进程并没有为它收尸(调用wait或waitpid来获得它的结束状态)的进程。

任何一个子进程(init除外)在exit后并非马上就消失,而是留下一个称外僵尸进程的数据结构,等待父进程处理。这是每个子进程都必需经历的阶段。另外子进程退出的时候会向其父进程发送一个SIGCHLD信号。

僵尸进程的目的?

设置僵死状态的目的是维护子进程的信息,以便父进程在以后某个时候获取。这些信息至少包括进程ID,进程的终止状态,以及该进程使用的CPU时间,所以当终止子进程的父进程调用wait或waitpid时就可以得到这些信息。如果一个进程终止,而该进程有子进程处于僵尸状态,那么它的所有僵尸子进程的父进程ID将被重置为1(init进程)。继承这些子进程的init进程将清理它们(也就是说init进程将wait它们,从而去除它们的僵尸状态)。

如何避免僵尸进程?

  1. 通过signal(SIGCHLD, SIG_IGN)通知内核对子进程的结束不关心,由内核回收。如果不想让父进程挂起,可以在父进程中加入一条语句:signal(SIGCHLD,SIG_IGN);表示父进程忽略SIGCHLD信号,该信号是子进程退出的时候向父进程发送的。
  2. 父进程调用wait/waitpid等函数等待子进程结束,如果尚无子进程退出wait会导致父进程阻塞waitpid可以通过传递WNOHANG使父进程不阻塞立即返回
  3. 如果父进程很忙可以用signal注册信号处理函数,在信号处理函数调用wait/waitpid等待子进程退出。
  4. 通过两次调用fork。父进程首先调用fork创建一个子进程然后waitpid等待子进程退出,子进程再fork一个孙进程后退出。这样子进程退出后会被父进程等待回收,而对于孙子进程其父进程已经退出所以孙进程成为一个孤儿进程,孤儿进程由init进程接管,孙进程结束后,init会等待回收。

第一种方法忽略SIGCHLD信号,这常用于并发服务器的性能的一个技巧因为并发服务器常常fork很多子进程,子进程终结之后需要服务器进程去wait清理资源。如果将此信号的处理方式设为忽略,可让内核把僵尸子进程转交给init进程去处理,省去了大量僵尸进程占用系统资源。

僵尸进程处理办法

1 wait()函数

#include <sys/types.h> 
#include <sys/wait.h>

pid_t wait(int *status);

进程一旦调用了wait,就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。 
参数status用来保存被收集进程退出时的一些状态,它是一个指向int类型的指针。但如果我们对这个子进程是如何死掉的毫不在意,只想把这个僵尸进程消灭掉,(事实上绝大多数情况下,我们都会这样想),我们就可以设定这个参数为NULL,就象下面这样:

  pid = wait(NULL);

如果成功,wait会返回被收集的子进程的进程ID,如果调用进程没有子进程,调用就会失败,此时wait返回-1,同时errno被置为ECHILD。

  • wait系统调用会使父进程暂停执行,直到它的一个子进程结束为止。
  • 返回的是子进程的PID,它通常是结束的子进程
  • 状态信息允许父进程判定子进程的退出状态,即从子进程的main函数返回的值或子进程中exit语句的退出码。
  • 如果status不是一个空指针,状态信息将被写入它指向的位置

可以上述的一些宏判断子进程的退出情况:

2 waitpid()函数

#include <sys/types.h> 
#include <sys/wait.h>

pid_t waitpid(pid_t pid, int *status, int options);

参数:

status:如果不是空,会把状态信息写到它指向的位置,与wait一样

options:允许改变waitpid的行为,最有用的一个选项是WNOHANG,它的作用是防止waitpid把调用者的执行挂起

The value of options is an OR of zero or more  of  the  following  con- 
stants:

WNOHANG     return immediately if no child has exited.

WUNTRACED   also  return  if  a  child  has stopped (but not traced via 
            ptrace(2)).  Status for traced children which have  stopped 
            is provided even if this option is not specified.

WCONTINUED (since Linux 2.6.10) 
            also return if a stopped child has been resumed by delivery 
            of SIGCONT.

返回值:如果成功返回等待子进程的ID,失败返回-1

回到顶部

对于waitpid的p i d参数的解释与其值有关:

pid == -1 等待任一子进程。于是在这一功能方面waitpid与wait等效。

pid > 0 等待其进程I D与p i d相等的子进程。

pid == 0 等待其组I D等于调用进程的组I D的任一子进程。换句话说是与调用者进程同在一个组的进程。

pid < -1 等待其组I D等于p i d的绝对值的任一子进程

回到顶部

wait与waitpid区别:

  • 在一个子进程终止前, wait 使其调用者阻塞,而waitpid 有一选择项,可使调用者不阻塞。
  • waitpid并不等待第一个终止的子进程—它有若干个选择项,可以控制它所等待的特定进程。
  • 实际上wait函数是waitpid函数的一个特例。waitpid(-1, &status, 0);

示例:

如以下代码会创建100个子进程,但是父进程并未等待它们结束,所以在父进程退出前会有100个僵尸进程。

#include <stdio.h>  
#include <unistd.h>  
   
int main() {  
   
  int i;  
  pid_t pid;  
   
  for(i=0; i<100; i++) {  
    pid = fork();  
    if(pid == 0)  
      break;  
  }  
   
  if(pid>0) {  
    printf("press Enter to exit...");  
    getchar();  
  }  
   
  return 0;  
}  

其中一个解决方法即是编写一个SIGCHLD信号处理程序来调用wait/waitpid来等待子进程返回。

#include <stdio.h>  
#include <unistd.h>  
#include <signal.h>  
#include <sys/types.h>  
#include <sys/wait.h>  
   
void wait4children(int signo) {  
   
  int status;  
  wait(&status);  
   
}  
   
int main() {  
   
  int i;  
  pid_t pid;  
   
  signal(SIGCHLD, wait4children);  
   
  for(i=0; i<100; i++) {  
    pid = fork();  
    if(pid == 0)  
      break;  
  }  
   
  if(pid>0) {  
    printf("press Enter to exit...");  
    getchar();  
  }  
   
  return 0;  
}  

但是通过运行程序发现还是会有僵尸进程,而且每次僵尸进程的数量都不定。这是为什么呢?其实主要是因为Linux的信号机制是不排队的,假如在某一时间段多个子进程退出后都会发出SIGCHLD信号,但父进程来不及一个一个地响应,所以最后父进程实际上只执行了一次信号处理函数。但执行一次信号处理函数只等待一个子进程退出,所以最后会有一些子进程依然是僵尸进程。

虽然这样但是有一点是明了的,就是收到SIGCHLD必然有子进程退出,而我们可以在信号处理函数里循环调用waitpid函数来等待所有的退出的子进程。至于为什么不用wait,主要原因是在wait在清理完所有僵尸进程后再次等待会阻塞。

所以最佳方案如下:

#include <stdio.h>  
#include <unistd.h>  
#include <signal.h>  
#include <errno.h>  
#include <sys/types.h>  
#include <sys/wait.h>  
   
void wait4children(int signo) {  
  int status;  
  while(waitpid(-1, &status, WNOHANG) > 0);  
}  
   
int main() {  
   
  int i;  
  pid_t pid;  
   
  signal(SIGCHLD, wait4children);  
   
  for(i=0; i<100; i++) {  
    pid = fork();  
    if(pid == 0)  
      break;  
  }  
   
  if(pid>0) {  
    printf("press Enter to exit...");  
    getchar();  
  }  
   
  return 0;  
}  

这里使用waitpid而不是使用wait的原因在于:我们在一个循环内调用waitpid,以获取所有已终止子进程的状态。我们必须指定WNOHANG选项,它告诉waitpid在有尚未终止的子进程在运行时不要阻塞。我们不能在循环内调用wait,因为没有办法防止wait在正运行的子进程尚有未终止时阻塞。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/190990.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java——字母大小写全排列

题目链接 字母大小写全排列 题目描述 给定一个字符串 s &#xff0c;通过将字符串 s 中的每个字母转变大小写&#xff0c;我们可以获得一个新的字符串。 返回 所有可能得到的字符串集合 。以 任意顺序 返回输出。 题目示例 输入&#xff1a;s “a1b2” 输出&#xff1a…

spring-boot国际化i18n中英文实现

一、背景 接触到的项目&#xff0c;使用国际化越来越多&#xff0c;系统页面实现中英文环境&#xff0c;记录下&#xff0c;前端使用vue vue-i8n 后端java自己封装 前端翻译对象&#xff1a;页面涉及到的所有按钮&#xff08;包括新增、删除、导出、下载、导入、上一页、下…

相控阵天线分析综合、设计与测试

目录概述HFSS特殊曲线天线建模直线阵列天线特性和阵列因子&#xff08;方向图乘积定理、波束扫描&#xff09;非规则直线阵列天线&#xff08;稀布阵列、稀疏阵列、平方率分布阵列&#xff09;直线阵列天线低副瓣综合&#xff08;切比雪夫、泰勒分布、SinZ-Z和Villeneuve分布&a…

2023-java面试最新总结

1. Java中的原始数据类型都有哪些&#xff0c;它们的大小及对应的封装类是什么&#xff1f; boolean boolean数据类型非true即false。这个数据类型表示1 bit&#xff0c;但是它的大小并没有精确定义。 《Java虚拟机规范》中如是说&#xff1a;“虽然定义了boolean这种数据类型…

胡歌官宣生女,胡椒粉们真为他高兴,人生最顶级的能力是【涅槃重生】的力量

今天刷到胡歌官宣生女&#xff0c;胡歌当父亲了&#xff0c;真为他高兴。恭喜老胡&#xff01;&#x1f389;作为十多年胡椒粉&#xff0c;连夜录制了这个视频&#xff08;抖音视频号&#xff1a;【小伍说_523的作品】胡歌官宣生女&#xff0c;十几年的胡椒粉真替他高兴&#x…

Unity二

一、脚本的创建 可以在project视图中的Assets文件夹中右键创建一个C#脚本&#xff0c;可以将脚本分类放在不同的文件夹中&#xff0c;或者点击游戏对象后在Inspector视图中找到最下方的添加组件按钮&#xff0c;然后点击新建脚本即可。若在project视图中创建的脚本&#xff0c…

详解HTTP请求行

请求行格式 request-linemethod SP request targe SP HTTP-version CRLFrequest-target 有四种格式 origin-formabsolute-path&#xff1a;向原服务器&#xff0c;也就是实际响应内容的服务器发起请求的&#xff0c;path为空时&#xff0c;必须传递 /absolute-formabsolute…

应用性能监控对系统慢访问分析案例

背景 某港口综合管控系统是其主要的业务系统&#xff0c;最近发现用户反馈出现访问响应慢的情况。 该港口已部署NetInside流量分析系统&#xff0c;使用流量分析系统提供实时和历史原始流量。本次分析重点针对综合管控业务系统性能进行分析&#xff0c;以供安全取证、性能分析…

C# 国际化问题之CultureInfo(小数点的不同一些欧洲国家习惯使用,而非.)

前言 在公司做的桌面软件在国内以及亚美洲地区都能正常使用&#xff0c;结果到了俄罗斯客户那边软件就根本打不开&#xff0c;不能正常使用。 文章目录定位问题分析问题找解决方案总结定位问题 经定位排查&#xff0c;最终发现是俄罗斯的浮点数不是用小数点&#xff0c;而是用…

网赚 美金 Neobux注册操作完整教程

neobux是点击网赚即时支付&#xff08;Instant pay&#xff09;型的创始网站&#xff0c;同时也是点击网赚类网站中信誉最高、实力最强&#xff0c;会员最多、最稳定的。Neobux官网 https://www.neobux.com/?rxyadmin账户注册1.点击官网地址进入neobux主站&#xff0c;点击右上…

【正点原子FPGA连载】第二十五章设备树下的LED驱动实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Linux开发指南

1&#xff09;实验平台&#xff1a;正点原子MPSoC开发板 2&#xff09;平台购买地址&#xff1a;https://detail.tmall.com/item.htm?id692450874670 3&#xff09;全套实验源码手册视频下载地址&#xff1a; http://www.openedv.com/thread-340252-1-1.html 第二十五章设备树…

BUUCTF[GXYCTF2019]Ping Ping Ping 1

题目 http://06590c75-6f53-45ea-b42d-b2034e4cd98e.node4.buuoj.cn:81/ 解题过程 1.尝试ping一下127.0.0.1 http://06590c75-6f53-45ea-b42d-b2034e4cd98e.node4.buuoj.cn:81/?ip127.0.0.1 2.尝试查看当前目录下的内容 http://06590c75-6f53-45ea-b42d-b2034e4cd98e.node…

vue项目采用vue-cli-plugin-cesium 插件集成cesium

市面上的前端框架中&#xff0c;VueCesium 可谓是最佳搭档&#xff0c;一般做 Cesium B 端产品的公司都会使用 Vue&#xff0c;所以后续内容都将基于 Vue通常情况下&#xff0c;我们要在 Vue 中使用 Cesium&#xff0c;首先要安装 Cesium&#xff0c;然后要在 vue-cli 的 webpa…

涂鸦发布蓝牙子设备框架,让智能家居快速实现毫秒级配网速度

如果有人问智能家居行业应用最广的协议是什么&#xff1f;蓝牙一定是选项之一。体积小便于集成、功耗低、全球适用等特征&#xff0c;让蓝牙设备被大范围应用。在消费侧则更关心的问题是&#xff1a;设备的配网速度、成功率以及设备连接的稳定性。 所以&#xff0c;市场要找到…

26对称矩阵及正定性

一、知识概要 本节从对称矩阵的特征值&#xff0c;特征向量入手&#xff0c;介绍对称矩阵在我们之前学习的一些内容上的特殊性质。并借此引出了正定矩阵。 二、对称矩阵 正如我们之前学习的很多特殊矩阵一样&#xff08;如马尔科夫矩阵&#xff09;&#xff0c;对称矩阵也有…

字符串流stringstream--<sstream>

字符串流stringstream流详解 一.stringstream是C提供的一个字符串流&#xff0c;与iostream和fstream的操作方法类似&#xff0c;只是功能不同。要使用字符串流必须包含其头文件<sstream>。 #include<sstream> 二.stringstream字符串流通常用来做数据转换&#x…

python-闭包知识点

目录标题1、函数参数2、闭包3、闭包的使用4、闭包内修改外部变量1、函数参数 def fun01():print(func01 is show) #fun01() 会输出打印结果 print(fun01)#输出&#xff1a;<function ox11....地址> #函数名存放的是函数所在空间的地址fun02 fun01 #函数名也可以像普通…

中国电子学会2021年12月份青少年软件编程Scratch图形化等级考试试卷三级真题(含答案)

青少年软件编程&#xff08;图形化&#xff09;等级考试试卷&#xff08;三级&#xff09; 分数&#xff1a;100 题数&#xff1a;38 一、单选题(共 25 题&#xff0c;共 50 分) 执行下列程序&#xff0c;屏幕上可以看到几只小猫&#xff1f;&#xff08;B &#xff09;(2…

奇怪的一些

1、请求200 但是请求回来的是html 网络里会写 200OK from disk cache 这个时候需要清除浏览器缓存 2、base64 引入失败 找一个js文件直接引入 require is not defined import Base64 from ‘***’ https://blog.csdn.net/u011383596/article/details/116712729 3、promise prom…

dataworks插入,分组,排序,关联语句

– 向表中插入数据 – insert into/overwrite table 表名 values (…),(…),…; – insert into/overwrite table 表名 select …; – 创建样例表 create table if not exists temp_test_xsxx_30( xh string comment ‘学号’, xm string comment ‘姓名’, xb string comment…