【僵尸进程】
【僵尸进程】
- 目录:
- 知识点
- 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 生成准确且最新的信息,而不会…...
测试微信模版消息推送
进入“开发接口管理”--“公众平台测试账号”,无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息: 关注测试号:扫二维码关注测试号。 发送模版消息: import requests da…...
OpenLayers 可视化之热力图
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 热力图(Heatmap)又叫热点图,是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...
连锁超市冷库节能解决方案:如何实现超市降本增效
在连锁超市冷库运营中,高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术,实现年省电费15%-60%,且不改动原有装备、安装快捷、…...
深入理解JavaScript设计模式之单例模式
目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式(Singleton Pattern&#…...
Python爬虫(二):爬虫完整流程
爬虫完整流程详解(7大核心步骤实战技巧) 一、爬虫完整工作流程 以下是爬虫开发的完整流程,我将结合具体技术点和实战经验展开说明: 1. 目标分析与前期准备 网站技术分析: 使用浏览器开发者工具(F12&…...
【git】把本地更改提交远程新分支feature_g
创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...
三体问题详解
从物理学角度,三体问题之所以不稳定,是因为三个天体在万有引力作用下相互作用,形成一个非线性耦合系统。我们可以从牛顿经典力学出发,列出具体的运动方程,并说明为何这个系统本质上是混沌的,无法得到一般解…...
BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践
6月5日,2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席,并作《智能体在安全领域的应用实践》主题演讲,分享了在智能体在安全领域的突破性实践。他指出,百度通过将安全能力…...
以光量子为例,详解量子获取方式
光量子技术获取量子比特可在室温下进行。该方式有望通过与名为硅光子学(silicon photonics)的光波导(optical waveguide)芯片制造技术和光纤等光通信技术相结合来实现量子计算机。量子力学中,光既是波又是粒子。光子本…...
STM32---外部32.768K晶振(LSE)无法起振问题
晶振是否起振主要就检查两个1、晶振与MCU是否兼容;2、晶振的负载电容是否匹配 目录 一、判断晶振与MCU是否兼容 二、判断负载电容是否匹配 1. 晶振负载电容(CL)与匹配电容(CL1、CL2)的关系 2. 如何选择 CL1 和 CL…...
