【Linux之进程间通信】05.僵尸进程

news/2024/11/25 21:56:40/

 
【Linux之进程间通信】

项目代码获取:https://gitee.com/chenshao777/linux-processes.git
(麻烦点个免费的Star哦,您的Star就是我的写作动力!)

05.僵尸进程

僵尸进程: 指的是进程终止后,资源没有得到回收,状态为 Z+
怎么创造一个僵尸进程出来呢,很简单,
通过 fork 函数创建一个子进程
父进程执行while(1)死循环,
子进程不循环,运行几条语句后直接退出,
例如:

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>/*僵尸进程 & 子进程给父进程发信号(通过kill(getppid(), sig)) & 子进程调用exit(),发送SIGCHLD信号给父进程命令: ./z+process现象:父进程正常运行,收到一次子进程的SIGUSR1信号子进程不调用exit()函数结束,则变成僵尸进程Z+子进程调用exit()函数结束,则向父进程发送SIGCHLD信号,父进程信号处理函数调用wait()函数回收子进程资源
*/
void handler(int arg)
{printf("信号处理handler,arg = %d\n", arg);  //arg是信号IDif(arg == SIGINT)printf("你按了 ctrl+c \n");if(arg == SIGUSR1)printf("父进程收到子进程信号\n");if(arg == SIGCHLD){printf("子进程终止时会向父进程发送SIGCHLD信号,告知父进程回收自己\n");wait(NULL);   //回收子进程资源}
}int main(int argc, char *argv[])
{signal(SIGINT, handler);   //SIGINT(ctrl+c)信号处理pid_t pid;pid = fork();if(pid > 0){signal(SIGUSR1, handler);  //接收子进程的信号// signal(SIGCHLD, handler);  //接收子进程结束的信号printf("父进程\n");for(int i = 0;i < 10;i++){printf("父 %d\n",i);sleep(1);}}if(pid == 0){printf("子进程运行,接着睡眠2秒\n");sleep(2);printf("给父进程发信号\n");kill(getppid(), SIGUSR1);sleep(1);printf("子进程自己隔屁了,子进程状态变化,自动向父进程发送SIGCHLD信号\n");}return 0;
}

本代码结合了 signal 信号处理函数,signal 函数的介绍可以借鉴一下我上一篇博客的第三小节 【Linux之进程间通信】04.Linux进程间的信号通信

运行结果:

hc@hc-vm:~/Linux_ARM/git/linux-processes/04.信号通信$ gcc 04.z+process.c -o z+process
hc@hc-vm:~/Linux_ARM/git/linux-processes/04.信号通信$ ./z+process 
父进程
父 0
子进程运行,接着睡眠2秒
父 1
给父进程发信号
父 2
信号处理handler,arg = 10
父进程收到子进程信号
父 3
子进程自己隔屁了,子进程状态变化,自动向父进程发送SIGCHLD信号
父 456789

子进程执行完语句直接结束运行,我们运行一下 ps -axj 命令,查看一下该进程的状态

  PPID    PID   PGID    SID TTY       TPGID STAT   UID   TIME COMMAND4070  30199  30199   4070 pts/1     30199 S+    1000   0:00 ./z+process30199  30200  30199   4070 pts/1     30199 Z+    1000   0:00 [z+process] <defunct>2282  30207  30207   2282 pts/0     30207 R+    1000   0:00 ps -axj

第一行是父进程信息,第二行是子进程信息
父进程是S+睡眠状态(可能正好在执行sleep函数)
子进程是Z+僵尸进程状态

处理僵尸进程:

首先我们要知道两个知识点:

1.wait()函数的作用:父进程一旦调用了wait就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经退出的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;

如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。
2.子进程的运行状态只要发生变化,都会自动向父进程发送一个SIGCHLD信号,所以父进程对SIGCHLD信号设置处理函数,在处理函数中使用wait()函数进行子进程的资源回收.

signal(SIGCHLD, handler);  //接收子进程结束的信号

上面的代码虽然说子进程退出是向父进程发送了SIGCHLD信号,但是该信号默认被忽略

---------------------------------------------
所以处理僵尸进程的方法:

1.在主进程中设置SIGCHLD信号处理函数
2.在信号处理函数中执行 wait(NULL),对退出的子进程进行资源回收,则僵尸进程不存在了。

具体操作:
将上面代码中
// signal(SIGCHLD, handler); //接收子进程结束的信号
的注释取消,再次运行,查看运行结果和进程状态:

hc@hc-vm:~/Linux_ARM/git/linux-processes/04.信号通信$ gcc 04.z+process.c -o z+process
hc@hc-vm:~/Linux_ARM/git/linux-processes/04.信号通信$ ./z+process 
父进程
父 0
子进程运行,接着睡眠2秒
父 1
给父进程发信号
信号处理handler,arg = 10
父进程收到子进程信号
父 23
子进程自己隔屁了,子进程状态变化,自动向父进程发送SIGCHLD信号
信号处理handler,arg = 17
子进程终止时会向父进程发送SIGCHLD信号,告知父进程回收自己
父 456789

运行结果多了一句 “子进程终止时会向父进程发送SIGCHLD信号,告知父进程回收自己”
说明子进程退出后主进程接收到了 SIGCHLD 信号,执行了 handler 函数中的以下代码

if(arg == SIGCHLD){printf("子进程终止时会向父进程发送SIGCHLD信号,告知父进程回收自己\n");wait(NULL);   //回收子进程资源
}

由于运行了 wait(NULL); 进程状态中也没有了僵尸进程,只有S+状态的主进程

  PPID    PID   PGID    SID TTY       TPGID STAT   UID   TIME COMMAND4070  30533  30533   4070 pts/1     30533 S+    1000   0:00 ./z+process2282  30535  30535   2282 pts/0     30535 R+    1000   0:00 ps -axj

扩展知识:

如果父进程提前结束,而子进程还在运行,则子进程退出时谁来给子进程回收资源呢?
答:如果子进程还在运行,父进程却提前退出,则PID为1的init进程会自动接管,成为该子进程的父进程,等子进程终止后回收资源 (Linux内核真牛)


http://www.ppmy.cn/news/75078.html

相关文章

( 动态规划) 115. 不同的子序列 ——【Leetcode每日一题】

❓115. 不同的子序列 难度&#xff1a;困难 给你两个字符串 s 和 t &#xff0c;统计并返回在 s 的 子序列 中 t 出现的个数。 题目数据保证答案符合 32 位带符号整数范围。 示例 1&#xff1a; 输入&#xff1a;s “rabbbit”, t “rabbit” 输出&#xff1a;3 解释&…

Linux---文件操作命令(touch、cat、more)

1. touch命令 可以通过touch命令创建文件 语法&#xff1a;touch [选项] Linux路径 touch命令&#xff0c;参数必填&#xff0c;表示要创建的文件路径&#xff0c;相对、绝对、特殊路径符均可以使用。 touch 命令不光可以用来创建文件&#xff08;当指定操作文件不存在时&a…

[比赛简介]Parkinson‘s Freezing of Gait Prediction

比赛链接&#xff1a;https://www.kaggle.com/competitions/tlvmc-parkinsons-freezing-gait-prediction 比赛简介 本次比赛的目标是检测步态冻结&#xff08;FOG&#xff09;&#xff0c;这是一种使人衰弱的症状&#xff0c;困扰着许多帕金森病患者。您将开发一个机器学习…

【内部类匿名内部类反射LambdaStream流的使用】

一、内部类和匿名内部类 匿名内内部类和lambda表达式主要的区别在于 1、匿名内部类他必须实现继承类的所有方法 2、匿名内部类在重写时有override标识&#xff0c;而lambda没有 3、lambda表达式继承的接口只能有一个抽象方法 在使用匿名内部类的过程中&#xff0c;我们需要注意…

2023年最新VMware 17+虚拟机详细配置安装【程序员使用指南】!!

文章目录 Vmware版本选择17Pro安装自定义安装填写对应的许可证正式安装虚拟机进行对应的配置配置镜像文件选择对应的语言&#xff1a;到这个界面&#xff0c;选择中文 安装结束连接对应的xshell Vmware版本选择17Pro安装 ● 最开始从这里出发 自定义安装 ● 记得自定义在自…

解决APP抓包问题「网络安全」

1.前言 在日常渗透过程中我们经常会遇到瓶颈无处下手&#xff0c;这个时候如果攻击者从APP进行突破&#xff0c;往往会有很多惊喜。但是目前市场上的APP都会为防止别人恶意盗取和恶意篡改进行一些保护措施&#xff0c;比如模拟器检测、root检测、APK加固、代码混淆、代码反调试…

使用JWT实现登录认证

一、介绍 1.1、Session、Cookie、Token区别 session&#xff1a;存储再服务端&#xff0c;无法引用与分布式场景&#xff0c;并且需要占用服务端的资源 cookie&#xff1a;存储再客户端&#xff0c;适用于分布式场景&#xff0c;但是存在安全问题&#xff0c;不支持垮域访问 t…

【多线程】什么是线程死锁?形成条件是什么?如何避免?

文章目录 一、什么是线程死锁二、线程死锁三、形成死锁的四个必要条件是什么四、如何避免线程死锁 一、什么是线程死锁 死锁是指两个或两个以上的进程&#xff08;线程&#xff09;在执行过程中&#xff0c;由于竞争资源或者由于彼此通信而造成的一种阻塞的现象&#xff0c;若…