【僵尸进程】
【僵尸进程】
- 目录:
- 知识点
- 1. 僵尸进程的定义
- 2. 僵尸进程产生的原因
- 3. 僵尸进程的危害
- 4. 如何避免僵尸进程
- 代码示例
- 产生僵尸进程的代码示例
- 避免僵尸进程的代码示例(父进程主动回收)
- 避免僵尸进程的代码示例(信号处理)
- 运行结果
- 产生僵尸进程的代码运行结果
- 避免僵尸进程的代码(父进程主动回收)运行结果
- 避免僵尸进程的代码(信号处理)运行结果
- 僵尸进程
- 原理
- 实现方法
- 1. 使用 `wait` 函数
- 2. 使用 `waitpid` 函数
- 代码示例
- 使用 `wait` 函数
- 使用 `waitpid` 函数
- 代码解释
- 会自动释放的资源
- 1. 内存资源
- 2. 文件描述符
- 3. 信号处理函数设置
- 不会自动释放的资源
- 1. 进程描述符
- 2. 一些共享资源
目录:
知识点
1. 僵尸进程的定义
在 Linux 系统中,当一个子进程结束运行(通过调用 exit
或接收到终止信号),它并不会立即从系统中消失。子进程会进入僵尸状态(Zombie State),此时子进程已经停止执行,但它的进程描述符(包含进程的退出状态等信息)仍然保留在系统中,直到其父进程调用 wait
或 waitpid
等系统调用获取其退出状态,释放该进程描述符,子进程才会真正被销毁。处于这种状态的进程就被称为僵尸进程。
2. 僵尸进程产生的原因
子进程先于父进程结束,而父进程没有及时调用 wait
或 waitpid
来回收子进程的资源,导致子进程的进程描述符一直保留在系统中,从而产生僵尸进程。
3. 僵尸进程的危害
僵尸进程虽然不占用系统的 CPU 资源,但它会占用系统的进程表项。如果系统中存在大量的僵尸进程,会导致进程表项被耗尽,从而无法创建新的进程,影响系统的正常运行。
4. 如何避免僵尸进程
- 父进程主动回收:父进程在子进程结束后,及时调用
wait
或waitpid
来回收子进程的资源。 - 信号处理:父进程可以通过捕获
SIGCHLD
信号,在信号处理函数中调用wait
或waitpid
来回收子进程的资源。 - 使用
sigaction
函数:结合sigaction
函数和SA_NOCLDWAIT
标志,避免子进程成为僵尸进程。
代码示例
产生僵尸进程的代码示例
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>int main() {pid_t pid = fork();if (pid < 0) {perror("fork");return 1;} else if (pid == 0) {// 子进程printf("子进程 (PID=%d) 即将退出\n", getpid());exit(0);} else {// 父进程printf("父进程 (PID=%d) 继续运行,不回收子进程资源\n", getpid());sleep(60); // 父进程休眠 60 秒,不回收子进程资源}return 0;
}
避免僵尸进程的代码示例(父进程主动回收)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>int main() {pid_t pid = fork();if (pid < 0) {perror("fork");return 1;} else if (pid == 0) {// 子进程printf("子进程 (PID=%d) 即将退出\n", getpid());exit(0);} else {// 父进程printf("父进程 (PID=%d) 等待子进程退出并回收资源\n", getpid());int status;pid_t child_pid = wait(&status);if (child_pid > 0) {if (WIFEXITED(status)) {printf("子进程 (PID=%d) 正常退出,退出状态码: %d\n", child_pid, WEXITSTATUS(status));}}}return 0;
}
避免僵尸进程的代码示例(信号处理)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>void sigchld_handler(int signum) {int status;pid_t pid;while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {if (WIFEXITED(status)) {printf("子进程 (PID=%d) 正常退出,退出状态码: %d\n", pid, WEXITSTATUS(status));}}
}int main() {signal(SIGCHLD, sigchld_handler);pid_t pid = fork();if (pid < 0) {perror("fork");return 1;} else if (pid == 0) {// 子进程printf("子进程 (PID=%d) 即将退出\n", getpid());exit(0);} else {// 父进程printf("父进程 (PID=%d) 继续运行,等待子进程退出信号\n", getpid());sleep(60);}return 0;
}
运行结果
产生僵尸进程的代码运行结果
子进程 (PID=1234) 即将退出
父进程 (PID=1233) 继续运行,不回收子进程资源
在父进程休眠的 60 秒内,使用 ps -ef | grep Z
命令可以查看到僵尸进程:
USER PID PPID C STIME TTY TIME CMD
youruser 1234 1233 0 10:00 pts/0 00:00:00 [a.out] <defunct>
其中 <defunct>
表示该进程是僵尸进程。
避免僵尸进程的代码(父进程主动回收)运行结果
子进程 (PID=1234) 即将退出
父进程 (PID=1233) 等待子进程退出并回收资源
子进程 (PID=1234) 正常退出,退出状态码: 0
此时使用 ps -ef | grep Z
命令不会查看到僵尸进程。
避免僵尸进程的代码(信号处理)运行结果
子进程 (PID=1234) 即将退出
父进程 (PID=1233) 继续运行,等待子进程退出信号
子进程 (PID=1234) 正常退出,退出状态码: 0
同样,使用 ps -ef | grep Z
命令不会查看到僵尸进程。
僵尸进程
在 Linux 系统中,父进程可以通过主动回收子进程资源的方式来避免僵尸进程的产生。下面为你详细介绍相关原理、实现方法以及代码示例。
原理
当子进程结束时,它会向父进程发送 SIGCHLD
信号,同时进入僵尸状态,此时子进程的进程描述符仍然保留在系统中,占用系统资源。父进程可以通过调用 wait
或 waitpid
系统调用获取子进程的退出状态,并释放其进程描述符,从而让子进程彻底从系统中消失。
实现方法
1. 使用 wait
函数
wait
函数会阻塞父进程,直到它的任意一个子进程结束。当子进程结束后,wait
函数会返回该子进程的进程 ID,并将子进程的退出状态存储在 status
参数中。
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>pid_t wait(int *status);
- 参数:
status
是一个指向整数的指针,用于存储子进程的退出状态。如果不需要获取退出状态,可以将其设置为NULL
。 - 返回值:成功时返回结束的子进程的进程 ID;出错时返回 -1。
2. 使用 waitpid
函数
waitpid
函数提供了更灵活的子进程回收方式,它可以指定要等待的子进程,还可以设置非阻塞模式。
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>pid_t waitpid(pid_t pid, int *status, int options);
- 参数:
pid
:指定要等待的子进程。取值有以下几种情况:pid > 0
:等待进程 ID 为pid
的子进程。pid == -1
:等待任意一个子进程,等同于wait
函数。pid == 0
:等待与调用进程同组的任意子进程。pid < -1
:等待进程组 ID 等于pid
绝对值的任意子进程。
status
:用于存储子进程的退出状态,与wait
函数的status
参数相同。options
:可以设置一些选项,常用的选项有:WNOHANG
:如果没有子进程结束,函数立即返回 0,而不会阻塞。WUNTRACED
:如果子进程处于暂停状态,函数也会返回。WCONTINUED
:如果子进程因收到SIGCONT
信号而继续运行,函数也会返回。
- 返回值:
- 如果
options
中设置了WNOHANG
,且没有子进程结束,返回 0。 - 成功时返回结束的子进程的进程 ID。
- 出错时返回 -1。
- 如果
代码示例
使用 wait
函数
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>int main() {pid_t pid = fork();if (pid < 0) {perror("fork");return 1;} else if (pid == 0) {// 子进程printf("子进程 (PID=%d) 即将退出\n", getpid());exit(0);} else {// 父进程printf("父进程 (PID=%d) 等待子进程退出并回收资源\n", getpid());int status;pid_t child_pid = wait(&status);if (child_pid > 0) {if (WIFEXITED(status)) {printf("子进程 (PID=%d) 正常退出,退出状态码: %d\n", child_pid, WEXITSTATUS(status));}}}return 0;
}
使用 waitpid
函数
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>int main() {pid_t pid = fork();if (pid < 0) {perror("fork");return 1;} else if (pid == 0) {// 子进程printf("子进程 (PID=%d) 即将退出\n", getpid());exit(0);} else {// 父进程printf("父进程 (PID=%d) 等待子进程退出并回收资源\n", getpid());int status;pid_t child_pid = waitpid(pid, &status, 0);if (child_pid > 0) {if (WIFEXITED(status)) {printf("子进程 (PID=%d) 正常退出,退出状态码: %d\n", child_pid, WEXITSTATUS(status));}}}return 0;
}
代码解释
在上述代码中,首先使用
fork
函数创建一个子进程。
- 子进程打印一条信息后调用
exit
函数退出。- 父进程打印一条信息后,调用
wait
或waitpid
函数等待子进程退出,并获取其退出状态。- 使用
WIFEXITED
宏判断子进程是否正常退出,如果是,则使用WEXITSTATUS
宏获取子进程的退出状态码并打印。
通过这种方式,父进程可以及时回收子进程的资源,避免僵尸进程的产生。
子进程在退出时会自动释放一部分资源,但并非所有资源都会自动释放,下面为你详细介绍:
会自动释放的资源
1. 内存资源
- 栈和堆内存:子进程在运行过程中在栈上分配的局部变量和在堆上通过
malloc
、calloc
、realloc
等函数分配的内存,在子进程退出时会被自动释放。例如,在子进程中使用malloc
分配了一块内存用于存储数据,当子进程退出时,操作系统会回收这块内存,防止内存泄漏。 - 数据段和代码段:子进程的代码段(存储程序的可执行指令)和数据段(存储全局变量和静态变量)所占用的内存空间也会在子进程退出时被释放。
2. 文件描述符
子进程在运行过程中打开的文件描述符(如文件、套接字等),在子进程退出时会被操作系统自动关闭 ,例如,子进程打开了一个文件进行读写操作,当子进程退出时,该文件描述符会被关闭,相应的文件资源会被释放。
3. 信号处理函数设置
子进程所设置的信号处理函数等相关设置在其退出时会失效,操作系统会清理这些与子进程相关的信号处理信息。
不会自动释放的资源
1. 进程描述符
子进程退出后,其进程描述符(包含进程的状态信息、退出状态、资源使用统计等)并不会自动释放。子进程会进入僵尸状态(Zombie State),此时它的进程描述符仍然保留在系统中,直到其父进程调用wait
或waitpid
等系统调用获取其退出状态,释放该进程描述符,子进程才会真正从系统中消失。如果父进程没有及时回收子进程的进程描述符,就会产生僵尸进程。
2. 一些共享资源
- 共享内存:如果子进程使用了共享内存(通过
shmget
、shmat
等函数创建或关联),在子进程退出时,共享内存本身不会自动释放。共享内存是多个进程可以共享的内存区域,子进程的退出不会影响其他进程对共享内存的使用,需要显式地调用shmdt
(分离共享内存)和shmctl
(删除共享内存)等函数来释放共享内存资源。 - 信号量:子进程使用的信号量(通过
semget
、semop
等函数操作)在其退出时也不会自动释放。信号量是用于进程间同步的机制,需要显式地调用semctl
等函数来删除信号量集。
综上所述,虽然子进程在退出时会自动释放一部分资源,但对于进程描述符和一些共享资源,需要父进程或其他进程进行显式的处理,以确保系统资源的正确释放和回收。
相关文章:

【僵尸进程】
【僵尸进程】 目录:知识点1. 僵尸进程的定义2. 僵尸进程产生的原因3. 僵尸进程的危害4. 如何避免僵尸进程 代码示例产生僵尸进程的代码示例避免僵尸进程的代码示例(父进程主动回收)避免僵尸进程的代码示例(信号处理) 运…...

【框架】参考 Spring Security 安全框架设计出,轻量化高可扩展的身份认证与授权架构
关键字:AOP、JWT、自定义注解、责任链模式 一、Spring Security Spring Security 想必大家并不陌生,是 Spring 家族里的一个安全框架,特别完善,但学习成本比较大,不少开发者都觉得,这个框架“很重” 他的…...

【Git 学习笔记_27】DIY 实战篇:利用 DeepSeek 实现 GitHub 的 GPG 密钥创建与配置
文章目录 1 前言2 准备工作3 具体配置过程3.1. 本地生成 GPG 密钥3.2. 导出 GPG 密钥3.3. 将密钥配置到 Git 中3.4. 测试提交 4 问题排查记录5 小结与复盘 1 前言 昨天在更新我的第二个 Vim 专栏《Mastering Vim (2nd Ed.)》时遇到一个经典的 Git 操作问题:如何在 …...
微信小程序地图map全方位解析
微信小程序地图map全方位解析 微信小程序的 <map> 组件是一个功能强大的工具,可以实现地图展示、定位、标注、路径规划等多种功能。以下是全方位解析微信小程序地图组件的知识点: 一、地图组件基础 1. 引入 <map> 组件 在页面的 .wxml 文…...
调试无痛入手
在调试过程中,Step In、Step Over 和 Step Out 是控制代码执行流程的常用操作,帮助开发者逐行或逐块检查代码行为。以下是它们的详细介绍及使用方法: 1. Step In 功能:进入当前行的函数或方法内部,逐行执行其代码。使…...
【蓝桥杯集训·每日一题2025】 AcWing 6135. 奶牛体检 python
6135. 奶牛体检 Week 1 2月21日 农夫约翰的 N N N 头奶牛站成一行,奶牛 1 1 1 在队伍的最前面,奶牛 N N N 在队伍的最后面。 农夫约翰的奶牛也有许多不同的品种。 他用从 1 1 1 到 N N N 的整数来表示每一品种。 队伍从前到后第 i i i 头奶牛的…...
AI发展迅速,是否还有学习前端的必要性?
今天有个小伙伴跟我讨论:“现在 AI 发展迅速,是否还有学习 JS 或者 TS 及前端知识的必要?” 我非常肯定地说: 是的,学习 JavaScript/TypeScript 以及前端知识仍然非常必要,而且在可预见的未来,…...

【数据标准】数据标准化是数据治理的基础
导读:数据标准化是数据治理的基石,它通过统一数据格式、编码、命名与语义等,全方位提升数据质量,确保准确性、完整性与一致性,从源头上杜绝错误与冲突。这不仅打破部门及系统间的数据壁垒,极大促进数据共享…...

VS2022配置FFMPEG库基础教程
1 简介 1.1 起源与发展历程 FFmpeg诞生于2000年,由法国工程师Fabrice Bellard主导开发,其名称源自"Fast Forward MPEG",初期定位为多媒体编解码工具。2004年后由Michael Niedermayer接任维护,逐步发展成为包含音视频采…...

three.js之特殊材质效果
*案例42 创建一个透明的立方体 <template><div ref"container" className"container"></div> </template><script setup> import * as THREE from three; import WebGL from three/examples/jsm/capabilities/WebGL.js // 引…...

Qt常用控件之日历QCalendarWidget
日历QCalendarWidget QCalendarWidget 是一个日历控件。 QCalendarWidget属性 属性说明selectDate当前选中日期。minimumDate最小日期。maximumDate最大日期。firstDayOfWeek设置每周的第一天是周几(影响日历的第一列是周几)。gridVisible是否显示日历…...

vxe-table 如何实现跟 Excel 一样的数值或金额的负数自动显示红色字体
vxe-table 如何实现跟 Excel 一样的数值或金额的负数自动显示红色字体,当输入的值为负数时,会自动显示红色字体,对于数值或者金额输入时该功能就非常有用了。 查看官网:https://vxetable.cn gitbub:https://github.co…...

DINOv2 + yolov8 + opencv 检测卡车的可拉拽雨覆是否完全覆盖
最近是接了一个需求咨询图像处理类的,甲方要在卡车过磅的地方装一个摄像头用检测卡车的车斗雨覆是否完全, 让我大致理了下需求并对技术核心做下预研究 开发一套图像处理软件,能够实时监控经过的卡车并判断其车斗的雨覆状态。 系统需具备以下…...

算法日记27:完全背包(DFS->记忆化搜索->倒叙DP->顺序DP->空间优化)
一、暴力搜索(DFS) O ( n 2 ) O(n^2) O(n2) 1.1)思路解析 1、注意和01背包的区别在于每个物品可以无限次选择 注意在完全背包中,当一个物品被选择过一次,我们仍然需要考虑是否继续选择这个物品 01背包: …...
Linux 命令大全完整版(14)
5. 文件管理命令 chgrp(change group) 功能说明:变更文件或目录的所属群组。语 法:chgrp [-cfhRv][–help][–version][所属群组][文件或目录…] 或 chgrp [-cfhRv][–help][–version][–reference<参考文件或目录>][文件或目录…]补充说明&…...

基于 DeepSeek LLM 本地知识库搭建开源方案(AnythingLLM、Cherry、Ragflow、Dify)认知
写在前面 博文内容涉及 基于 Deepseek LLM 的本地知识库搭建使用 ollama 部署 Deepseek-R1 LLM知识库能力通过 Ragflow、Dify 、AnythingLLM、Cherry 提供理解不足小伙伴帮忙指正 😃,生活加油 我站在人潮中央,思考这日日重复的生活。我突然想,…...
Could not initialize class io.netty.util.internal.Platfor...
异常信息: Exception in thread "main" java.lang.NoClassDefFoundError: Could not initialize class io.netty.util.internal.PlatformDependent0 Caused by: java.lang.ExceptionInInitializerError: Exception java.lang.reflect.InaccessibleObjec…...

【书生大模型实战营】玩转HF/魔搭/魔乐社区-L0G4000
本文是书生大模型实战营系列的第4篇,本文的主题是:玩转HF/魔搭/魔乐社区。 1.开源大模型社区总览 开源不仅仅是一种技术模式,更是一种精神的体现。它打破了知识的壁垒,让技术平权成为可能。近年来,开源大模型社区蓬勃…...
2025年华为手机解锁BL的方法
注:本文是我用老机型测试的,新机型可能不适用 背景 华为官方已经在2018年关闭了申请BL解锁码的通道,所以华为手机已经无法通过官方获取解锁码。最近翻出了一部家里的老手机华为畅玩5X,想着能不能刷个系统玩玩,但是卡…...

了解 RAG 第二部分:经典 RAG 的工作原理
在本系列的第一篇文章中,我们介绍了检索增强生成 (RAG) ,解释了扩展传统大型语言模型 (LLM)功能的必要性。我们还简要概述了 RAG 的核心思想:从外部知识库检索上下文相关的信息,以确保 LLM 生成准确且最新的信息,而不会…...

css实现圆环展示百分比,根据值动态展示所占比例
代码如下 <view class""><view class"circle-chart"><view v-if"!!num" class"pie-item" :style"{background: conic-gradient(var(--one-color) 0%,#E9E6F1 ${num}%),}"></view><view v-else …...

【JavaEE】-- HTTP
1. HTTP是什么? HTTP(全称为"超文本传输协议")是一种应用非常广泛的应用层协议,HTTP是基于TCP协议的一种应用层协议。 应用层协议:是计算机网络协议栈中最高层的协议,它定义了运行在不同主机上…...

Appium+python自动化(十六)- ADB命令
简介 Android 调试桥(adb)是多种用途的工具,该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具,其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利,如安装和调试…...
在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:
在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档,…...

汽车生产虚拟实训中的技能提升与生产优化
在制造业蓬勃发展的大背景下,虚拟教学实训宛如一颗璀璨的新星,正发挥着不可或缺且日益凸显的关键作用,源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例,汽车生产线上各类…...
第25节 Node.js 断言测试
Node.js的assert模块主要用于编写程序的单元测试时使用,通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试,通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...

论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)
笔记整理:刘治强,浙江大学硕士生,研究方向为知识图谱表示学习,大语言模型 论文链接:http://arxiv.org/abs/2407.16127 发表会议:ISWC 2024 1. 动机 传统的知识图谱补全(KGC)模型通过…...

使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台
🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...

GC1808高性能24位立体声音频ADC芯片解析
1. 芯片概述 GC1808是一款24位立体声音频模数转换器(ADC),支持8kHz~96kHz采样率,集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器,适用于高保真音频采集场景。 2. 核心特性 高精度:24位分辨率,…...

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中,新增了一个本地验证码接口 /code,使用函数式路由(RouterFunction)和 Hutool 的 Circle…...