【Linux】详解僵尸进程与孤儿进程(Z僵死状态引发的内存泄漏与处理办法)

75e194dacf184b278fe6cf99c1d32546.jpeg

🌈 个人主页:谁在夜里看海.

🔥 个人专栏:《C++系列》《Linux系列》《算法系列》

⛰️ 丢掉幻想,准备斗争

d047c7b1ef574257b8397fe5cc5c290b.gif

目录

引言

一、僵尸进程

1.子进程的创建与退出

2.进程表

3.僵尸状态产生

4.直观感受一下:

二、孤儿进程

1.产生原因

2.处理办法

3.直观感受一下:

总结


引言

在上一篇关于Linux的章节中,我们介绍了进程的各种状态:R运行状态、S睡眠状态、D磁盘休眠状态、T停止状态、X死亡状态。其实还有一种特殊的运行状态,叫做Z僵死状态,为什么有僵死和死亡两种不同的状态呢?僵死并不等同于死亡

有人说,一个人肉体的死亡并不算真正的死亡,因为他还被人记着,直到世界上没有人记得他的时候,这个人才是完全意义上死亡了。僵死状态就是这个道理,一个进程已经退出了,但是它的父进程并没有读取到子进程退出的代码,这时就会产生僵死进程(子进程死亡了但是父进程还记得它)

那么为什么父进程还会记得已经“死亡”的子进程呢?这就要从父子进程的工作原理讲起了:

一、僵尸进程

1.子进程的创建与退出

父进程使用fork()系统调用来创建一个新的子进程。fork()函数会返回两次:在父进程中返回子进程的PID,在子进程中返回0,父进程和子进程是各自独立执行代码的:

#include <iostream>
#include <unistd.h>
using namespace std;int main() {pid_t pid = fork();  // 创建子进程if (pid < 0) {cerr << "子进程创建失败!" << endl;return 1;}if (pid == 0) {  // 子进程执行的代码cout << "子进程正在运行,PID为:" << getpid() << endl;return 0;  // 子进程退出} else {  // 父进程执行的代码cout << "父进程正在运行,PID为:" << getpid() << endl;return 0;  // 父进程退出}
}

c344ff976b9c493c94a5a3e8ed69300f.png

当执行到fork()时,会生成一个新进程。如果fork()成功,父进程和子进程都会继续执行。子进程的 PID 将返回给父进程,而在子进程中,fork()返回 0。

2.进程表

子进程在完成自身任务并退出时,操作系统并没有立即将其从进程表中移除。子进程变为“已终止”状态,但它会保留在进程表中,直到父进程收集它的退出状态。

进程表是操作系统用来管理所有进程的核心数据结构。每个进程都有一个进程控制块(PCB),其中包含了进程的各种信息,包括其父进程的 ID(PPID)和进程的状态。

为什么需要存储运行的状态?

父进程通过查看进程表,获取子进程的状态,根据不同的状态执行不同操作。

提问:父进程获取子进程状态是为了帮助子进程进行内存资源释放吗?

回答:并不是!子进程在执行结束之后,会自行释放内存资源,即使子进程错误中断了,没有自行释放资源,操作系统也会帮其释放资源。

那么父进程收集子进程状态信息的作用是什么呢?下面是几种常见的原因:

处理子进程执行失败时的错误恢复

父进程可以根据子进程的退出状态判断子进程是否正常完成任务。如果子进程由于某些错误退出,父进程可以选择执行错误恢复操作。例如:

假设父进程是一个文件处理程序,它会启动多个子进程来处理不同的文件。如果一个子进程处理某个文件时失败了(退出码非零),父进程就可以通过获取退出状态来判断这一点,然后采取以下行动:

①:重新启动该子进程来处理失败的文件。

②:记录错误日志,并通知用户某个文件处理失败。

③:跳过失败的文件,继续处理其他文件。

决定是否继续或终止任务

父进程需要知道子进程的执行结果,以决定是否继续执行其他任务或者终止整个操作。例如:

父进程启动多个子进程来执行并行任务,每个子进程执行不同的计算任务。父进程可能需要决定是否继续执行其他任务,或者如果某个子进程失败,是否停止其他所有任务:

如果某个子进程退出码表明它执行失败,父进程可以选择终止所有其他子进程,避免浪费计算资源。

如果所有子进程都成功,父进程可以汇总结果并继续进行后续步骤。

资源清理和收尾操作 

有些程序需要父进程在子进程结束后做一些额外的资源清理工作。例如,父进程可能会在子进程执行任务时分配某些资源(内存、文件句柄等),需要在子进程退出后根据退出状态来执行适当的清理工作。例如:

假设父进程管理一个服务器,启动子进程来处理客户端请求。子进程在处理请求时可能会打开临时文件或者分配内存,父进程需要知道子进程是否正常退出,以便执行以下操作:

如果子进程正常退出,父进程可以清理相关资源,比如关闭临时文件、释放内存。

如果子进程异常退出,父进程可能需要保留错误日志或者重新处理请求。

上述这些情况都在说明父子进程的协作关系:父进程和子进程并非完全独立,而是有相互依赖的关系,父进程需要回收子进程的资源,操作系统会协调这一过程。 

3.僵尸状态产生

父进程具体是如何查看到子进程的运行条目等信息的呢?

父进程通过调用 wait()waitpid() 来回收子进程的退出状态。当子进程退出并且父进程调用 wait() 时,父进程执行后续操作,然后操作系统会清除子进程在进程表中的条目。

那么问题又来了:进程中断后,操作系统会为其释放内存资源(理解为擦屁股),那么父进程没有调用wait()时,操作系统还会清除条目等信息吗?答案是不会:

操作系统保留进程表条目,是为了确保父进程能够获取子进程的退出状态,执行相应的操作。操作系统不自动删除这些条目,因为父进程需要在适当的时候通过系统调用(如 wait())来获取子进程的退出信息。所以,如果父进程没有调用wait()获取信息,操作系统就不会释放进程条目这些资源,这就会导致内存泄漏,这是操作系统故意这么设计的,目的就是为了保证父进程可以收集到子进程的条目信息!

子进程执行完毕之后,父进程没有通过wait()收集进程条目信息,就会导致条目信息不会被释放,造成内存泄漏,此时这个存在内存泄漏的进程也叫做僵尸进程。

4.直观感受一下:

下面这段代码就是演示僵尸进程的产生过程:

  1 #include <iostream>2 #include <unistd.h>3 #include <sys/wait.h>4 #include <ctime>5 using namespace std;6  7 int main()8 {9   pid_t pid = fork();10   if(pid<0)11     cout<<"子进程创建失败!"<<endl;12   else if(pid == 0)13   {14     cout<<"子进程正在运行,pid为:"<<getpid()<<e    ndl;15     sleep(5);16     cout<<"子进程结束!"<<endl;17   }18   else 19   {20     cout<<"父进程正在运行,pid为:"<<getpid()<<e    ndl;21     sleep(30); // 父进程没有结束就不会调用wait()22     cout<<"父进程调用wait,回收子进程..."<<endl;23     wait(NULL);24     cout<<"完成回收!"<<endl;25   }26   return 0;27 }

b71bbbc847be4e1d8bb6205d074e46eb.png

可以看到,此时子进程执行结束,父进程还未调用wait(),僵尸进程(Z状态)就产生了

f0e298fe21ef4f81b3e75b24ebf72167.png 父进程调用玩wait()对子进程条目信息收集完毕之后,操作系统为其释放资源,进程解除僵尸状态 

二、孤儿进程

1.产生原因

上面提到僵尸进程产生是由于父进程没有调用wait()导致的,处理办法就是让父进程必须调用wait(),但是有这么一种特殊情况:

子进程还在执行,可是父进程提前执行完毕了,之后子进程执行完毕时,已经没有父进程来为它调用wait()了,进程条目资源就不会被系统释放,导致了内存泄漏。父进程提前结束的进程称为孤儿进程。

面对孤儿进程这种情况(内存泄漏),操作系统采用了一种巧妙的处理办法:

2.处理办法

init进程

操作系统对孤儿进程的处理办法就是将孤儿进程托管给init进程,让init进程调用wait(),实现进场条目资源的释放,避免内存泄漏,那么init进程是何方神圣,作用这么大呢:

init 是 Unix 和类 Unix 操作系统中的第一个进程,它在操作系统启动时由内核启动,并且是所有其他进程的祖先。每个系统中至少有一个 init 进程,它是系统中最为重要的进程之一,负责系统初始化和其他进程的管理。

init 进程的生命周期非常长,可以说是操作系统中唯一一个始终存在的进程,直到系统关机或重启。因此将孤儿进程托管给它,一定可以保证资源的正常释放

3.直观感受一下:

下面这段代码可以清除展示孤儿进程的产生与init进程的托管过程:

  1 #include<iostream>2 #include<unistd.h>3 #include<sys/wait.h>4 #include<ctime>5 #include<cstdlib>6 using namespace std;7 8 int main()9 {10   pid_t pid = fork();11   if(pid<0)12     cout<<"子进程创建失败!"<<endl;13   else if(pid == 0)14   {15     cout<<"子进程正在运行,pid为:"<<getpid()<<endl;16     cout<<"父进程ppid为:"<<getppid()<<endl;17     sleep(10);18     cout<<"子进程正在运行,pid为:"<<getpid()<<endl;19     cout<<"父进程ppid为:"<<getppid()<<endl;20     sleep(5);21     cout<<"子进程结束!"<<endl;22     return 0;23   }24   else25   {26     sleep(5);27     cout<<"父进程结束!pid为:"<<getpid()<<endl;28     exit(0);29   }30   return 0;                                                    31 }

e9da1ed7b47b4b08a754897e7cc8b56e.png

init进程是操作系统的第一个进程,所以它的PID为1,我们可以观察到,子进程的父进程结束后,其的确被托管给init进程了(父进程变成了init进程) 

总结

本篇篇博客详细讲解了 僵尸进程孤儿进程 的产生过程与处理办法。僵尸进程产生于子进程退出后,父进程未调用 wait() 收集其退出状态,导致进程表中的信息未被清理,从而造成资源泄漏。孤儿进程则是父进程提前结束,子进程在没有父进程的情况下继续执行,操作系统将其交给 init 进程处理,以确保资源的正常回收。


以上就是【详解僵尸进程与孤儿进程】的全部内容,欢迎指正~ 

码文不易,还请多多关注支持,这是我持续创作的最大动力!  

 

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

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

相关文章

基于微信小程序的科学健身助手的设计与实现

&#x1f497;博主介绍&#x1f497;&#xff1a;✌在职Java研发工程师、专注于程序设计、源码分享、技术交流、专注于Java技术领域和毕业设计✌ 温馨提示&#xff1a;文末有 CSDN 平台官方提供的老师 Wechat / QQ 名片 :) Java精品实战案例《700套》 2025最新毕业设计选题推荐…

【Pytorch】torch.utils.data模块

torch.utils.data模块主要用于进行数据集处理&#xff0c;是常用的一个包。在构建数据集的过程中经常会用到。要使用data函数必须先导入&#xff1a; from torch.utils import data 下面介绍几个经常使用到的类。 torch.utils.data.DataLoader DataLoader(dataset, batch_…

Qt在designer设计师界面使用QSplitter

QSplitter控件是解决软件不同分辨率自适应布局的好帮手之一&#xff0c;该控件无法在控件列表找到&#xff0c;使用条件较为隐蔽 环境 qt5.12.12 MSVC2017 1.新建一个qt项目&#xff0c;初始自带Mainwindow&#xff0c;拖两个qwidget进来&#xff0c;注意此时centralWidget处于…

北京申请中级职称流程(2024年)

想找个完整详细点的申请流程资料真不容易&#xff0c;做个分享送给需要的人吧。 不清楚为什么说文章过度宣传&#xff0c;把链接和页面去掉了&#xff0c;网上自己找一下。 最好用windows自带的EDGE浏览器打开申请网站&#xff0c;只有在开始申请的时间内才可以进行网上申报&…

测评部署和管理 WordPress 最方便的面板

新版宝塔面板快速搭建WordPress新手教程 - 倚栏听风-Morii - 博客园 初学者使用1Panel面板快速搭建WordPress网站 - 倚栏听风-Morii - 博客园 可以看到&#xff0c;无论是宝塔还是1Panel&#xff0c;部署和管理WordPress都有些繁琐&#xff0c;而且还需要额外去配置Nginx和M…

ubuntu:20.04安装协议逆向工具netzob

创建容器 docker run -d --name ubuntu_env ubuntu:20.04 /bin/bash -c "while true; do sleep 1; done" 63a8f5cf5431a930671ff0e7bb2b667adf001efb05fd7261da244879d2699bec 进入容器 PS E:\src> docker exec -it ubuntu_env /bin/bash 安装常用工具 apt upda…

SpringBoot实现WebSocket

参考链接&#xff1a;https://www.kancloud.cn/king_om/mic_03/2783864 一、环境搭建 1.创建SpringBoot项目&#xff0c;引入相关依赖 <dependencies><!-- Spring Boot核心启动器&#xff0c;引入常用依赖基础 --><dependency><groupId>org.springf…

uni-app快速入门(八)--常用内置组件(上)

uni-app提供了一套基础组件&#xff0c;类似HTML里的标签元素&#xff0c;不推荐在uni-app中使用使用div等HTML标签。在uni-app中&#xff0c;对应<div>的标签是view&#xff0c;对应<span>的是text&#xff0c;对应<a>的是navigator&#xff0c;常用uni-app…

早期超大规模语言模型的尝试——BLOOM模型论文解读,附使用MindSpore和MindNLP的模型和实验复现

背景 预训练语言模型已经成为了现代自然语言处理pipeline中的基石&#xff0c;因为其在少量的标注数据上产生更好的结果。随着ELMo、ULMFiT、GPT和BERT的开发&#xff0c;使用预训练模型在下游任务上微调的范式被广泛使用。随后发现预训练语言模型在没有任何额外训练的情况下任…

【团购核销】抖音生活服务商家应用快速接入②——商家授权

文章目录 一、前言二、授权流程三、授权Url3.1 Url参数表3.2 授权能力表3.3 源码示例 四、授权回调4.1 添加授权回调接口4.2 授权回调接口源码示例 五、实际操作演示六、参考 一、前言 目的&#xff1a;将抖音团购核销的功能集成到我们自己开发的App和小程序中 【团购核销】抖音…

解决 Android 单元测试 No tests found for given includes:

问题 报错&#xff1a; Execution failed for task :testDebugUnitTest. > No tests found for given includes: 解决方案 1、一开始以为是没有给测试类加public修饰 2、然后替换 Test 注解的包可以解决&#xff0c;将 org.junit.jupiter.api.Test 修改为 org.junit.Tes…

gbase8s之mysql的show命令实现

第一步&#xff1a;生成show.sh脚本 cat /home/gbase/show.sh #!/bin/bash #作者&#xff1a;乡村野中医 #创作时间2024-11-21 #脚本名称show.sh function show(){ #echo $# if [ "xdatabases" x$1 ] then echo "select name from sysdatabases;"|…

android 实现答题功能

一、效果 二、实现思路 1、界面实现 实现起来其实不难&#xff0c;首先我们可以看到&#xff0c;界面是由答题进度、题目、选项ABCD组成&#xff0c;现在就是要考虑实现方式&#xff0c;答题进度可以使用Textviewprogressbar实现&#xff0c;题目直接使用Textview&#xff0c;…

正排索引和倒排索引

一、简介 正排索引&#xff1a;一个未经处理的数据库中&#xff0c;一般是以文档ID作为索引&#xff0c;以文档内容作为记录。 倒排索引&#xff1a;Inverted index&#xff0c;指的是将单词或记录作为索引&#xff0c;将文档ID作为记录&#xff0c;这样便可以方便地通过单词或…

Django一分钟:django中收集关联对象关联数据的方法

场景&#xff1a;我有一个模型&#xff0c;被其它多个模型关联&#xff0c;我配置了CASCADE级联删除&#xff0c;我想要告知用户删除该实例之后&#xff0c;哪些关联数据将会被一同删除。 假设我们当前有这样一组模型&#xff1a; class Warehouse(models.Model):""…

卷积神经网络各层介绍

目录 1 卷积层 2 BN层 3 激活层 3.1 ReLU&#xff08;Rectified Linear Unit&#xff09; 3.2 sigmoid 3.3 tanh&#xff08;双曲正切&#xff09; 3.4 Softmax 4 池化层 5 全连接层 6 模型例子 1 卷积层 卷积是使用一个卷积核&#xff08;滤波器&#xff09;对矩阵进…

【工控】线扫相机小结 第四篇

背景 这一片主要是对第三篇继续补充。话说上一篇讲到了两种模式的切换&#xff0c;上一篇还遗留了一个Bug&#xff0c;在这一篇里进行订正&#xff01; 代码回顾 /// <summary>/// 其实就是打开触发/// </summary>void SetLineSacanWorkMode(){-----首先设置为帧…

AI 大模型重塑软件开发的未来

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

【软考】系统架构设计师-计算机系统基础(4):计算机网络

计算机网络功能&#xff1a;数据通信、资源共享、管理集中化、分布式处理、负载均衡 5G高峰速率&#xff1a;10Gbit/s 广域网&#xff08;因特网&#xff09;/城域网/局域网&#xff08;以太网&#xff09; 总线型&#xff1a;利用率低&#xff0c;易冲突&#xff0c;干扰大…

Ultiverse 和web3新玩法?AI和GameFi的结合是怎样

Gamef 和 AI 是我们这个周期十分看好两大赛道之一&#xff0c;(Gamef 拥有极强的破圈效应&#xff0c;引领 Web2 用户进军 Web3 最佳利器。AI是这个周期最热门赛道&#xff0c;无论 Web2的 OpenAl&#xff0c;还是 Web3&#xff0c;都成为话题热议焦点。那么结合 GamefiA1双叙事…