当前位置: 首页 > 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…...

[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?

&#x1f9e0; 智能合约中的数据是如何在区块链中保持一致的&#xff1f; 为什么所有区块链节点都能得出相同结果&#xff1f;合约调用这么复杂&#xff0c;状态真能保持一致吗&#xff1f;本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里&#xf…...

进程地址空间(比特课总结)

一、进程地址空间 1. 环境变量 1 &#xff09;⽤户级环境变量与系统级环境变量 全局属性&#xff1a;环境变量具有全局属性&#xff0c;会被⼦进程继承。例如当bash启动⼦进程时&#xff0c;环 境变量会⾃动传递给⼦进程。 本地变量限制&#xff1a;本地变量只在当前进程(ba…...

树莓派超全系列教程文档--(62)使用rpicam-app通过网络流式传输视频

使用rpicam-app通过网络流式传输视频 使用 rpicam-app 通过网络流式传输视频UDPTCPRTSPlibavGStreamerRTPlibcamerasrc GStreamer 元素 文章来源&#xff1a; http://raspberry.dns8844.cn/documentation 原文网址 使用 rpicam-app 通过网络流式传输视频 本节介绍来自 rpica…...

以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:

一、属性动画概述NETX 作用&#xff1a;实现组件通用属性的渐变过渡效果&#xff0c;提升用户体验。支持属性&#xff1a;width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项&#xff1a; 布局类属性&#xff08;如宽高&#xff09;变化时&#…...

【项目实战】通过多模态+LangGraph实现PPT生成助手

PPT自动生成系统 基于LangGraph的PPT自动生成系统&#xff0c;可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析&#xff1a;自动解析Markdown文档结构PPT模板分析&#xff1a;分析PPT模板的布局和风格智能布局决策&#xff1a;匹配内容与合适的PPT布局自动…...

关键领域软件测试的突围之路:如何破解安全与效率的平衡难题

在数字化浪潮席卷全球的今天&#xff0c;软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件&#xff0c;这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下&#xff0c;实现高效测试与快速迭代&#xff1f;这一命题正考验着…...

Pinocchio 库详解及其在足式机器人上的应用

Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库&#xff0c;专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性&#xff0c;并提供了一个通用的框架&…...

HubSpot推出与ChatGPT的深度集成引发兴奋与担忧

上周三&#xff0c;HubSpot宣布已构建与ChatGPT的深度集成&#xff0c;这一消息在HubSpot用户和营销技术观察者中引发了极大的兴奋&#xff0c;但同时也存在一些关于数据安全的担忧。 许多网络声音声称&#xff0c;这对SaaS应用程序和人工智能而言是一场范式转变。 但向任何技…...

消息队列系统设计与实践全解析

文章目录 &#x1f680; 消息队列系统设计与实践全解析&#x1f50d; 一、消息队列选型1.1 业务场景匹配矩阵1.2 吞吐量/延迟/可靠性权衡&#x1f4a1; 权衡决策框架 1.3 运维复杂度评估&#x1f527; 运维成本降低策略 &#x1f3d7;️ 二、典型架构设计2.1 分布式事务最终一致…...

Vue3中的computer和watch

computed的写法 在页面中 <div>{{ calcNumber }}</div>script中 写法1 常用 import { computed, ref } from vue; let price ref(100);const priceAdd () > { //函数方法 price 1price.value ; }//计算属性 let calcNumber computed(() > {return ${p…...