💐 🌸 🌷 🍀 🌹 🌻 🌺 🍁 🍃 🍂 🌿 🍄🍝 🍛 🍤
📃个人主页 :阿然成长日记 👈点击可跳转
📆 个人专栏: 🔹数据结构与算法🔹C语言进阶🔹C++🔹Liunx
🚩 不能则学,不知则问,耻于问人,决无长进
🍭 🍯 🍎 🍏 🍊 🍋 🍒 🍇 🍉 🍓 🍑 🍈 🍌 🍐 🍍
文章目录
- 一、什么是僵尸状态?
- 二、为什么要有僵尸状态?
- 三、写个代码,感受一下
- !wait 阻塞函数作用
- 四、子进程需要回收退出信息,那父进程呢?
- 五、 <font color="#660000">为什么平常见不到Z状态呢?
- 六、僵尸进程有什么危害呢?【内存泄露】
- 七、如何预防僵尸进程不被父进程回收
一、什么是僵尸状态?
⭐️用一个生动形象的比喻来描述僵尸状态:
- 学校星期五最后一节课大扫除,我们每次打扫完成后,不会立即就回家🏡,而是必须等待老师👱来
验收
完成后我才能回家。 - 进程完成后,不能立即退出(回家🏡),必须等待父进程(老师👱)来获取子进程的信息后才能彻底释放掉。那这个等待的过程与僵尸状态十分相似。
二、为什么要有僵尸状态?
学习这里需要一些前提知识~~进程退出码
简单来说就是一个子进程进程既然被创建出来,那么一定是带着任务来的,你的任务完成情况需要有人来读取,否则,你做的是无用功啊。就好比老板让你干事,你干完就溜了,也不汇报一下完成情况怎么样,老板也不知道你到底是干了没有。
三、写个代码,感受一下
这个代码主要意思就是。子进程和父进程一起执行三秒,三秒之后呢,子进程正常退出,父进程继续执行。
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4
5 int main(void)
6 {
7 pid_t id = fork();
8 if(id == 0)
9 {
10 // child
11 int cnt = 3;
12 while(cnt)
13 {
14 printf("I am child, pid: %d, ppid: %d, cnt: %d\n", getpid(), getppid(), cnt);
15 sleep(1);
16
17 cnt--;
18 }
19 exit(0);
20 }
21 else
22 {
23 // father
24 while(1)
25 {
26 printf("I am father, pid: %d, ppid: %d\n", getpid(), getppid());
27 sleep(1);
28 }
29 }
30 return 0;
31 }
就会得到如下信息:
❗️注意:如果子进程退出,而父进程并没有调用wait
或waitpid
获取子进程的状态信息(通俗讲就是回收子进程),那么子进程的进程描述符仍然保存在系统中。
- 这也是上面程序执行后,我们为什么能看到Z状态的原因。
!wait 阻塞函数作用
1️⃣ .阻塞并等待子进程退出
2️⃣ .回收子进程残留资源
3️⃣ .获取子进程结束状态(退出原因)
4️⃣.调用一次只能回收一个子进程
四、子进程需要回收退出信息,那父进程呢?
千万不要误解以为使用fork()函数创建一个子进程,那么父进程就没人管了,所有进程都有一个顶级的父亲bash
,也就是经常看到的一个进程PID为27414
的这么一个进程。所以,就算是父进程它退出时依然会先进入僵尸状态
–被bash读取相关退出信息后再释放它的资源–进入死亡状态
五、 为什么平常见不到Z状态呢?
所有的进程退出时,一定是
先经历Z
(僵尸状态),才会到X
(死亡状态)。但是由于操作系统的速度十分快
,进程一退出就立马有父进程来回收子进程信息了,就像前面学习运行状态时,我们总是能看到睡眠状态,而运行状态会一闪而过或是直接不显示了
六、僵尸进程有什么危害呢?【内存泄露】
进程一般退出的时候,一般其不会立即彻底退出,它的代码和相关数据已经释放,但是,它的task_struct对象
依然还在内存中,仍然占用内存
!!。如果父进程没有主动回收子进程信息,子进程会一直
让自己处于Z状态,这也是为了方便后续父进程读取子进程的相关退出结果。
那如果一个父进程一直不去不去回收已经退出的子进程的信息,就会造成 内存泄露
。
七、如何预防僵尸进程不被父进程回收
1️⃣ 让父进程调用 wait 或 waitpid 函数来回收子进程的资源,并获取其退出状态。
2️⃣ 让父进程忽略 SIGCHLD信号,这样子进程结束后会被自动回收,不会变成僵尸进程。
3️⃣杀死父进程,让子进程成为孤儿进程,由 init 进程接管并回收。