一、什么是僵死进程?
一般情况下,程序调用exit(包括_exit和_Exit,它们的区别这里不做解释),它的绝大多数内存和相关的资源已经被内核释放掉,但是在进程表中这个进程项(entry)还保留着(进程ID,退出状态,占用的资源等等),你可能会问,为什么这么麻烦,直接释放完资源不就行了吗?这是因为有时它的父进程想了解它的退出状态 。在子进程退出但还未被其父进程“收尸”之前,该子进程就是僵死进程,或者僵尸进程 。如果父进程先于子进程去世,那么子进程将被init进程收养,这个时候init就是这个子进程的父进程 。
所以一旦出现父进程长期运行,而又没有显示调用wait或者waitpid,同时也没有处理SIGCHLD信号,这个时候init进程就没有办法来替子进程收尸,这个时候,子进程就真的成了“僵尸”了 。
二、僵死进程与孤儿进程的区别?
回答这个问题很简单,就是爸爸(父进程)和儿子(子进程)谁先死的问题!
如果当儿子还在世的时候,爸爸去世了,那么儿子就成孤儿了,这个时候儿子就会被init收养,换句话说,init进程充当了儿子的爸爸,所以等到儿子去世的时候,就由init进程来为其收尸 。
如果当爸爸还活着的时候,儿子死了,这个时候如果爸爸不给儿子收尸,那么儿子就会变成僵尸进程 。
三、僵死进程的危害?
- 僵死进程的PID还占据着,意味着海量的子进程会占据满进程表项,会使后来的进程无法fork.
- 僵死进程的内核栈无法被释放掉(1K 或者 2K大小),为啥会留着它的内核栈,因为在栈的最低端,有着thread_info结构,它包含着 struct_task 结构,这里面包含着一些退出信息 。
网上搜了下,总结有三种方方法:
① 程序中显示的调用signal(SIGCHLD, SIG_IGN)来忽略SIGCHLD信号,这样子进程结束后,由内核来wai和释放资源
② fork两次,第一次fork的子进程在fork完成后直接退出,这样第二次fork得到的子进程就没有爸爸了,它会自动被老祖宗init收养,init会负责释放它的资源,这样就不会有“僵尸”产生了
③ 对子进程进行wait,释放它们的资源,但是父进程一般没工夫在那里守着,等着子进程的退出,所以,一般使用信号的方式来处理,在收到SIGCHLD信号的时候,在信号处理函数中调用wait操作来释放他们的资源 。
五、对每个避免僵死进程方法的解析与总结
首先我们让我们来看一个生成僵尸进程的程序zombie.c如下:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> int main(int argc, const char *argv[]) { int i; pid_t pid; for (i = 0; i < 10; i++) { if ((pid = fork()) == 0) /* child */ _exit(0); } sleep(10); exit(EXIT_SUCCESS); }
运行程序,在10s睡眠期间使用ps查看进程,你会发现有10个标记为“defunct”的僵尸进程:文章插图
接下来看第一种方法,程序avoid_zombie1.c如下:
#include <stdio.h> #include <stdlib.h> #include <signal.h> #include <unistd.h> #include <errno.h> int main(int argc, const char *argv[]) { pid_t pid; if (SIG_ERR == signal(SIGCHLD, SIG_IGN)) { perror("signal error"); _exit(EXIT_FAILURE); } while (1) { if ((pid = fork()) == 0) /* child */ _exit(0); } exit(EXIT_SUCCESS); }
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 10个很棒的JavaScript库,提升Web开发效率
- 科技之手点点,有机茶产业技术升级开发示范项目通过验收
- 用 testdisk 恢复 Linux 上已删除的文件
- 记一次使用next.js开发官网经历
- 使用 GNU bc 在 Linux Shell 中进行数学运算
- springcloud微服务架构开发实战:分布式消息总线
- 开发古茶山旅游,通关古茶山
- 谈谈如何学习Linux
- 如何获取Linux或者macOS系统版本相关信息
- 小程序云开发支持公众号网页开发了