当前位置: 首页 > news >正文

僵尸(Zombie)进程

文章目录

  • 1.僵尸进程
  • 2.产生僵尸进程的原因
  • 3.利用 wait 函数销毁僵尸进程
  • 4.使用 waitpid 函数销毁僵尸进程

1.僵尸进程

进程完成工作后(执行完 main 函数中的程序后)应被销毁,但有时这些进程将变成僵尸进程,占用系统中的重要资源。这种状态下的进程称作“僵尸进程”,这也是给系统带来负担的原因之一。我们应该消灭这种进程。当然应掌握正确的方法,否则它会死灰复燃。

从下图可以看到,父进程的 PID 是 1166,子进程的 PID 是 1167

在这里插入图片描述

如果我们使用 kill -9 1167 命令将子进程干掉,观察父进程会收到什么信号?

从下图可以看到,父进程收到了 SIGCHLD 信号,子进程变成了僵尸进程。

在这里插入图片描述

在这里插入图片描述

在 Unix 系统中,一个子进程结束了,但是他的父进程还活着,但该父进程没有调用 wait()/waitpid() 函数来进行额外的处置,那么这个子进程就会变成一个僵尸进程。

僵尸进程已经被终止,不干活了,但是依旧没有被内核丢弃掉,因为内核认为父进程可能还需要该子进程的一些信息。作为开发者,坚决不允许僵尸进程的存在。

如何干掉僵尸进程?

  • 重启电脑。
  • 手动地把僵尸进程的父进程干掉,僵尸进程就会自动消失。
  • 一个进程被终止或者停止时,SIGCHLD 信号会被发送给父进程,所以,对于源码中有 fork() 行为的进程,我们应该拦截并处理 SIGCHLD 信号。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>// 信号处理函数
void sig_usr(int signo)
{int status;switch (signo){case SIGUSR1:printf("收到了SIGUSR1信号,进程id=%d!\n", getpid());    break;case SIGCHLD:printf("收到了SIGCHLD信号,进程id=%d!\n", getpid());// waitpid()函数获取子进程的终止状态,这样子进程就不会成为僵尸进程了// 第一个参数为-1,表示等待任何子进程// 第二个参数保存子进程的状态信息// 第三个参数提供额外选项,WNOHANG表示不要阻塞,让这个waitpid()立即返回pid_t pid = waitpid(-1, &status, WNOHANG);if (pid == 0) return; // 子进程没结束,会立即返回这个数字if (pid == -1) return; // 这表示这个waitpid()调用有错误,有错误也立即返回出去return; // 走到这里,表示成功,那也return吧break;}
}int main(int argc, char* const* argv)
{pid_t pid;printf("进程开始执行!\n");// 系统函数,第一个参数是个信号,第二个参数是个函数指针,代表一个针对该信号的捕捉处理函数if (signal(SIGUSR1, sig_usr) == SIG_ERR){printf("无法捕捉SIGUSR1信号!\n");exit(1);}if (signal(SIGCHLD, sig_usr) == SIG_ERR){printf("无法捕捉SIGCHLD信号!\n");exit(1);}pid = fork(); // 创建一个子进程// 要判断子进程是否创建成功if (pid < 0){printf("子进程创建失败,很遗憾!\n");exit(1);}// 现在父进程和子进程同时开始运行了 for (;;){sleep(1);printf("休息1秒,进程id=%d!\n", getpid());}printf("再见了!\n");return 0;
}

在这里插入图片描述

2.产生僵尸进程的原因

向 exit 函数传递的参数值和 main 函数的 return 语句返回的值都会传递给操作系统。而操作系统不会销毁子进程,直到把这些值传递给产生该子进程的父进程。处在这种状态下的进程就是僵尸进程。也就是说,将子进程变成僵尸进程的正是操作系统。

问:此僵尸进程何时被销毁呢?

答:应该向创建子进程的父进程传递子进程的 exit 参数值或 return 语句的返回值。

问:如何向父进程传递这些值呢?

答:操作系统不会主动把这些值传递给父进程。只有父进程主动发起请求(函数调用)时,操作系统才会传递该值。换言之,如果父进程未主动要求获得子进程的结束状态值,操作系统将一直保存,并让子进程长时间处于僵尸进程状态。也就是说,父母要负责收回自己生的孩子。

接下来的示例将创建僵尸进程。

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>int main(int argc, char *argv[])
{pid_t pid = fork();if (pid == 0)    // if Child Process{puts("Hi, I am a child process");}else{// 输出子进程ID。可以通过该值查看子进程状态(是否为僵尸进程)。printf("Child Process ID: %d\n", pid);// 父进程暂停30秒。如果父进程终止,处于僵尸状态的子进程将同时销毁。因此,延缓父进程的执行以验证僵尸进程。sleep(30);    // Sleep 30 sec.}if (pid == 0)puts("End child process");elseputs("End parent process");return 0;
}

编译运行:

gcc zombie.c -o zombie
./zombie

在这里插入图片描述

程序开始运行后,将在如上所示状态暂停。跳出这种状态前(30秒内),应验证子进程是否为僵尸进程,该验证在其他控制台窗口进行。

在这里插入图片描述

可以看出,PID 为 1387 的进程状态为僵尸进程(Z+)。另外,经过 30 秒的等待时间后,PID 为 1386 的父进程和 PID 为 1387 的僵尸子进程同时销毁。

3.利用 wait 函数销毁僵尸进程

如前所述,为了销毁子进程,父进程应主动请求获取子进程的返回值。

#include <sys/wait.h>pid_t wait(int *statloc);// 成功时返回终止的子进程ID,失败时返回-1

调用此函数时如果已有子进程终止,那么子进程终止时传递的返回值(exit 函数的参数值、main 函数的 return 返回值)将保存到该函数的参数所指内存空间。但函数参数指向的单元中还包含其他信息,因此需要通过下列宏进行分离:

  • WIFEXITED:子进程正常终止时返回“真”(true)。
  • WEXITSTATUS:返回子进程的返回值。

也就是说,向 wait 函数传递变量 status 的地址时,调用 wait 函数后应编写如下代码:

if (WIFEXITED(status))    // 是正常终止的吗?
{puts("Normal termination!");printf("Child pass num: %d", WEXITSTATUS(status));    // 那么返回值是多少?
}

根据上述内容编写如下示例,在下面示例中,不会再让子进程变成僵尸进程。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>int main(int argc, char *argv[])
{int status;// 第11行创建的子进程将在第15行通过main函数中的return语句终止。pid_t pid = fork();if (pid == 0){return 3;}else{printf("Child PID: %d\n", pid);// 第22行中创建的子进程将在第26行通过调用exit函数终止。pid = fork();if (pid == 0){exit(7);}else{printf("Child PID: %d\n", pid);// 调用wait函数。之前终止的子进程相关信息将保存到status变量,同时相关子进程被完全销毁。wait(&status);// 第36行中通过WIFEXITED宏验证子进程是否正常终止。如果正常退出,则调用WEXITSTATUS宏输出子进程的返回值。if (WIFEXITED(status))printf("Child send one: %d\n", WEXITSTATUS(status));// 因为之前创建了2个进程,所以再次调用wait函数和宏。wait(&status);if (WIFEXITED(status))printf("Child send two: %d\n", WEXITSTATUS(status));// 为暂停父进程终止而插入的代码。此时可以查看子进程的状态。sleep(30); // Sleep 30 sec.}}return 0;
}

编译运行:

gcc wait.c -o wait
./wait

输出结果:

在这里插入图片描述

在这里插入图片描述

可以看出,此时系统中并没有 PID 为 1497 和 1498 的进程,这是因为调用了 wait 函数,完全销毁了该子进程。另外,两个子进程终止时返回的 333777 传递到了父进程。

这就是通过调用 wait 函数消灭僵尸进程的方法。调用 wait 函数时,如果没有已终止的子进程,那么程序将阻塞(Blocking)直到有子进程终止,因此要谨慎调用该函数。

4.使用 waitpid 函数销毁僵尸进程

wait 函数会引起程序阻塞,还可以考虑调用 waitpid 函数。这是防止僵尸进程的第二种方法,也是防止阻塞的方法。

#include <sys/wait.h>pid_t waitpid(pid_t pid, int *statloc, int options);// 成功时返回终止的子进程ID(或0),失败时返回-1
// pid:等待终止的目标子进程的ID,若传递-1,则与wait函数相同,可以等待任意子进程终止
// statloc:与wait函数的statloc参数具有相同含义
// options:传递头文件sys/wait.h中声明的常量WNOHANG,即使没有终止的子进程也不会进入阻塞状态,而是返回0并退出函数

下面介绍调用 waitpid 函数的示例。调用 waitpid 函数时,程序不会阻塞。

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>int main(int argc, char *argv[])
{int status;pid_t pid = fork();if (pid == 0){// 调用sleep函数推迟子进程的执行。这会导致程序延迟15秒。sleep(15);return 24;}else{// while循环中调用waitpid函数。向第三个参数传递WNOHANG,因此,若之前没有终止的子进程将返回0。while (!waitpid(-1, &status, WNOHANG)){sleep(3);puts("sleep 3sec.");}if (WIFEXITED(status))printf("Child send %d\n", WEXITSTATUS(status));}return 0;
}

编译运行:

gcc waitpid.c -o waitpid
./waitpid

输出结果:

在这里插入图片描述

可以看出,第 222222 行共执行了 555 次。另外,这也证明 waitpid 函数并未阻塞。

相关文章:

僵尸(Zombie)进程

文章目录1.僵尸进程2.产生僵尸进程的原因3.利用 wait 函数销毁僵尸进程4.使用 waitpid 函数销毁僵尸进程1.僵尸进程 进程完成工作后&#xff08;执行完 main 函数中的程序后&#xff09;应被销毁&#xff0c;但有时这些进程将变成僵尸进程&#xff0c;占用系统中的重要资源。这…...

JS实现:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少?

题目&#xff1a;有一对兔子&#xff0c;从出生后第3个月起每个月都生一对兔子&#xff0c;小兔子长到第三个月后每个月又生一对兔子&#xff0c;假如兔子都不死&#xff0c;问每个月的兔子总数为多少&#xff1f; 数列是 1,1,2,3,5,8,13,21....观察可以看出来从第三个数字开始…...

Verilog如何编写一个基础的Testbench

本文将讲述如何使用Verilog 编写一个基础的测试脚本&#xff08;testbench&#xff09;。在考虑一些关键概念之前&#xff0c;先来看看testbench的架构是什么样的。架构包括建模时间、initial块&#xff08;initial block&#xff09;和任务&#xff08;task&#xff09;。此文…...

基于JavaEE社区物业管理系统开发与实现(附源码资料)

文章目录1. 适用人群2. 你将收获3.项目简介4.技术栈5.测试账号6.部分功能模块展示6.1.管理员6.2.业主1. 适用人群 本课程主要是针对计算机专业相关正在做毕业设计或者是需要实战项目的Java开发学习者。 2. 你将收获 提供&#xff1a;项目源码、项目文档、数据库脚本、软件工…...

问一下ChatGPT:DIKW金字塔模型

经常看到这张DIKW金字塔模型图&#xff0c;还看到感觉有点过份解读的图&#xff0c;后面又加上了insight&#xff0c;impact等内容。 Data&#xff1a;是数据&#xff0c;零散的、无规则的呈现到人们眼前&#xff0c;如果你只看到这些数字&#xff0c;如果没有强大的知识背景&a…...

javaScript基础面试题 ---闭包

闭包1、闭包是什么&#xff1f;2、闭包可以解决什么问题&#xff1f;3、闭包的缺点1、闭包是什么&#xff1f; 闭包是一个函数加上到创建这个函数的作用域的链接&#xff0c;就是一个作用域可以访问到另一个作用域的变量&#xff0c;闭包‘关闭’了函数的自由变量 function f…...

如何自定义您的网站实时聊天图标

实时聊天图标是您网站上的一个按钮&#xff0c;可在访问者单击时打开实时聊天。它代表了您的企业与客户沟通的门户。这是您的网站访问者与您联系、提出问题和接收个性化推荐的一种方式&#xff0c;聊天图标的设计最好是简单且引人入胜&#xff0c;个性化的图标往往更能提现企业…...

Vue侦听器Watch

31. Vue侦听器Watch 1. 定义 Watch是Vue.js提供的一个观察者模式&#xff0c;用于监听数据的变化并执行相应的回调函数。虽然计算属性Computed在大多数情况下更合适&#xff0c;但有时也需要一个自定义的侦听器Watch。因为在有些情况下&#xff0c;我们需要在状态变化时执行一…...

云快充研发中心平台架构师谈云原生稳定性建设之路

作者&#xff1a;吕周洋 大家好&#xff0c;我是来自云快充研发中心的平台架构师吕周洋&#xff0c;今天我给大家分享云快充云原生稳定性之路。 点击查看&#xff1a;云快充研发中心平台架构师 吕周洋&#xff1a;云快充云原生稳定性治理之路 云快充成立于2016年&#xff0c…...

ENVI IDL学习笔记之基本操作

前言ENVI IDL&#xff08;交互式数据语言&#xff09;是一个通用的科学计算包&#xff0c;它提供了一套数学函数、数据分析工具&#xff0c;以及一些科学可视化和动画工具。IDL 是 ENVI 图像处理和分析软件的基础&#xff0c;可用于编写脚本并自动执行许多使用 ENVI 图形用户界…...

多线程面试题

1. Sychronized的锁升级过程是怎样的&#xff1f; 2. Tomcat 中为什么要使用自定义类加载器&#xff1f; 3. 说说对线程安全的理解 4. 对守护线程的理解 5. 并发、并行、串行之间的区别 6. Java死锁如何避免&#xff1f; 7. 谈谈你对AQS的理解&#xff0c;AQS如何实现可重入锁&…...

YARN运行流程

YARN是Hadoop资源管理器&#xff0c;他是一个通用资源管理平台和调度平台&#xff0c;可为上层应用提供统一的资源管理和调度&#xff0c;MapReduce等运算程序则相当于运行于操作系统上的应用程序&#xff0c;YARN为这些程序提供运算所需的资源内存、cpu。 YARN并不清楚用户提…...

java八股系列——SpringMVC从接受请求到完成响应的过程

Spring的MVC框架是围绕一个DispatcherServlet来设计的&#xff0c;这个Servlet会把请求分发给各个处理器&#xff0c;并支持可配置的处理器映射、视图渲染、本地化、时区与主题渲染等&#xff0c;甚至还能支持文件上传。 流程大致如下&#xff1a; 用户发起请求&#xff1a;用…...

Elasticsearch索引全生命周期

索引(Index)是Elasticsearch中最重要的概念之一&#xff0c;也是整个Elasticsearch操作的基础&#xff0c;它是相互关联的文档的一个集合。在Elasticsearch种&#xff0c;数据存储为 JSON 文档&#xff0c;每个文档将一组键&#xff08;字段或属性的名称&#xff09;与其对应的…...

汇编指令学习(LOOP)

一、xor异或操作&#xff0c;相同为0&#xff0c;不同为1xor eax,eaxeax异或eax&#xff0c;相同为0&#xff0c;并把结果存放到eax&#xff0c;简单说该语句就是想eax寄存器清零。二、ECX&#xff0c;计数器mov ecx,0x3将ecx寄存器设置为3三、DEC减一操作dec ecxecx寄存器的值…...

Linux 配置本地yum源

挂载光盘 进入包 配置路径&#xff0c;查看在线yum源 移动在线yum源到/home/目录下 进入vi,任意取名以.repo结尾即可 按住i进行编辑&#xff0c;输入以下内容 注意gpgcheck1是检验&#xff0c;配置本地yum源不需要检验 写入上图内容按住&#xff1a;输入wq&#xff0c;点击回车…...

【PyTorch】教程:torch.nn.LeakyReLU

torch.nn.LeakyReLU 原型 CLASS torch.nn.LeakyReLU(negative_slope0.01, inplaceFalse) 参数 negative_slope (float) – 控制负值斜率&#xff0c;默认为 1e-2inplace (bool) – in-place 操作&#xff0c;默认为 False 定义 LeakyReLU(x)max⁡(0,x)negative_slope∗min⁡…...

【刷题】-- 基础 -- 二分查找

精于结构、敏于心智、熟于代码 方式&#xff1a;对于会的代码&#xff1a;学会以最快的速度构建&#xff0c;并以最快的速度书写&#xff1b;对于不会的代码&#xff1a;学会&#xff08;以最短的路径下&#xff09;看懂别人的代码。学会使用参考文档、熟悉每一个容器。 刷题位…...

Spark MLlib 特征工程

Spark MLlib 特征工程预处理特征选择归一化离散化Embedding向量计算特征工程制约了模型效果 : 决定了模型效果的上限 , 而模型调优只是在不停地逼近上限好的特征工程才能有好的模型 特征处理函数分类 : 预处理 : 将模型无法直接消费的数据&#xff0c;转为可消费的数据形式特…...

CentOS7 完全卸载 php

在 CentOS 7 使用 yum install 简单安装 php 后&#xff0c;发现 php 版本 5.4 &#xff0c;太低了&#xff01; 然后&#xff0c;使用 yum remove 简单卸载后&#xff0c;发现 php 还在&#xff0c;不干净&#xff01; 只好 rpm 慢慢卸载 rpm -qa |grep php php-gd-5.4.16-4…...

关于OCS认证里必须知晓的内容

【关于OCS认证里必须知晓的内容】美国非营利组织Textile Exchange推出的有机认证标准——有机含量标准(The Organic Content Standard)&#xff0c;简称OCS。该标准通过跟踪有机原材料的种植从而监管整个有机产业链。OCS将应用于各种有机种植原料的验证&#xff0c;而不只限于有…...

创业做电商难不难?新人做电商怎么才能挣钱?

这几年经济不景气&#xff0c;创业做电商的人越来越多&#xff0c;但是&#xff0c;对于多数人来说&#xff0c;一开始做电商&#xff0c;都是试错成本&#xff0c;没有系统学习或者是跟着半吊子二把刀学的&#xff0c;结果赔钱就算了&#xff0c;新人创业做电商到底难不难&…...

【项目设计】高并发内存池(七)[性能测试和提升]

&#x1f387;C学习历程&#xff1a;入门 博客主页&#xff1a;一起去看日落吗持续分享博主的C学习历程博主的能力有限&#xff0c;出现错误希望大家不吝赐教分享给大家一句我很喜欢的话&#xff1a; 也许你现在做的事情&#xff0c;暂时看不到成果&#xff0c;但不要忘记&…...

PHP:Laravel cast array json数据存数据库时unicode 编码问题和update更新不触发数据转换

目录问题描述问题解决方式一&#xff1a;自定义属性方式二&#xff1a;继承覆写方式三&#xff1a;trait复用方式四&#xff1a;定义Cast子类update不生效参考文章问题描述 Model示例 class UserModel extends Model {protected $table tb_user;protected $casts [alias …...

自动化测试总结--断言

采购对账测试业务流程中&#xff0c;其中一个测试步骤总是失败&#xff0c;原因是用例中参数写错及断言不明确 一、问题现象&#xff1a; 采购对账主流程中&#xff0c;其中一个步骤失败了&#xff0c;会导致这个套件一直失败 图&#xff08;1&#xff09;测试报告视图中&…...

传输线的物理基础(三):传输线的瞬时阻抗

每个信号都有一个上升时间 RT&#xff0c;通常是从 10% 到 90% 的电压电平测量的。当信号沿传输线向下移动时&#xff0c;前沿在传输线上展开并具有空间范围。如果我们可以冻结时间并观察电压分布向外移动时的大小&#xff0c;我们会发现类似下图的东西。传输线上上升时间的长度…...

第六章:多线程

第六章&#xff1a;多线程 6.1&#xff1a;程序、进程、线程基本概念 程序 程序program是为了完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码&#xff0c;静态对象。 进程 ​ 进程process是程序的一次执行过程&#xff0c;或是正在运行的一个程序。是一个…...

铁路与公路

蓝桥杯集训每日一题acwing4074 某国家有 n 个城市&#xff08;编号 1∼n&#xff09;和 m 条双向铁路。 每条铁路连接两个不同的城市&#xff0c;没有两条铁路连接同一对城市。 除了铁路以外&#xff0c;该国家还有公路。 对于每对不同的城市 x,y&#xff0c;当且仅当它们之…...

GitHub Copilot 全新升级,工作效率提升 55%

2021年 6 月&#xff0c;GitHub 和 OpenAI 推出了 GitHub Copilot 预览版&#xff0c;可根据命名或者正在编辑的代码上下文为开发者提供代码建议&#xff0c;被称为“你的 AI 结对程序员”。 近日&#xff0c;GitHub 宣布&#xff0c;经过去年 12 月以来的短暂测试后&#xff…...

【IoT】《天道》中音响案例的SWOT分析

在20世纪80年代初&#xff0c;SWOT最初是由美国知名管理学教授海因茨韦里克提出的。 之后这个工具就经常被用于企业的战略分析、竞争对手分析等场景。 在每年例行的公司产品规划过程中&#xff0c;我个人也经常使用这个工具。 由于涉及一些公司商业上的信息&#xff0c;下面会用…...

武汉做网站知名的公司/网站设计制作公司

add_host 在playbook执行的过程中&#xff0c;动态的添加主机到指定的主机组中 常用参数&#xff1a; groups&#xff1a;添加主机至指定的组name&#xff1a;要添加的主机名或IP地址 示例&#xff1a; - name: add a host to group webservershosts: webserverstasks:- add…...

做策划的人经常浏览的网站/seo关键词首页排名代发

小白机器学习基础算法学习必经之路作者简介&#xff1a;武博士&#xff0c;人工智能方向博士&#xff0c;中国移动集团IT架构师。 科研方向&#xff1a;自然语言处理、计算机视觉、强化学习。 已经发表SCI文章3篇。 CSDN专栏文章60篇。&#xff08;机器学习专栏、深度学习专栏、…...

网站做的比较好的公司吗/seo好学吗入门怎么学

修改tomcat/conf目录里面server.xml文件 例如下面这样新增一个8090端口&#xff0c;设置下appBase目录&#xff0c;这样就可以用一个tomcat监听多个端口&#xff0c;每个端口都可以放应用了。我这样新增下面这个配置以后&#xff0c;tomcat就监听了2个端口&#xff08;8080&…...

哪些网站可以做淘宝客/推广软文范文800字

mysqli我们在PHP中可以使用mysqli扩展与MySQL数据库信息交流&#xff0c;i表示改进&#xff0c;增强&#xff0c;它执行速度更快。mysqli扩展被封装到一个类中&#xff0c;它是一种面向对象的技术&#xff0c;不过喜欢过程化编程的用户也不用担心&#xff0c;mysqli也提供了一个…...

网站一年的 运营费用/百度注册公司地址

var tiles L.tileLayer(http://{s}.tile.osm.org/{z}/{x}/{y}.png, {attribution: © <a href"http://osm.org/copyright">OpenStreetMap</a> contributors, }).addTo(map);...

做网站找个人还是找公司/品牌营销与推广

本节书摘来异步社区《淘宝店铺设计装修一册通》一书中的第2章&#xff0c;作者&#xff1a; 葛存山 责编&#xff1a; 赵轩, 更多章节内容可以访问云栖社区“异步社区”公众号查看。 2.4 创建文字 淘宝店铺设计装修一册通Photoshop提供了丰富的文字工具&#xff0c;可以在图像…...