关于僵尸进程
- 僵尸进程的相关知识点
- 僵尸进程的产生原因
- 僵尸状态的作用
- 僵尸进程的解决
- wait函数
- 代码示例
- waitpid及其使用
- 通过信号消灭僵尸进程
- 写在后面
僵尸进程的相关知识点
僵尸进程的产生原因
僵尸进程是什么?
进程完成工作后应被销毁,但有时候这些进程将变成僵尸进程,占用系统中的重要资源。这种状态下的进程称作“僵尸进程”
我们都知道,进程的常用三种状态是
- 就绪
- 运行
- 等待
而实际上,进程的状态信息如下
enum proc_state { UNUSED, EMBRYO, SLEEPING,
RUNNABLE, RUNNING, ZOMBIE };
// 僵尸状态:一个进程可以处于已退出但尚未清理的最终(final)状态
而运行结束或者被中止生命周期的进程就会进入僵尸状态zombie
。在linux通过ps -ef
可以查看到,处于僵尸进程的会显示为
僵尸进程产生的原因:
了解清楚了僵尸进程是什么(一种占用资源的、不可运行的、应该销毁的进程)后。我们需要分析,它怎么来的。为什么进程结束了不直接销毁。
向exit函数传递的参数值和main函数的return语句返回的值都会传递给操作系统。而操作系统不会销毁子进程,直到把这些值传递给产生该子进程的父进程。处在这种状态下的进程就是僵尸进程。
将子进程变成僵尸进程的是操作系统
僵尸状态的作用
有同学可能要和我最开始一样,子进程都运行结束了。还有什么用啊。还占着内存空间等资源。还需要我们父进程去处理。(白发人送黑发人)
但是,在读过相关书籍《操作系统导论》后。我就发现,僵尸进程还真有点用
这个最终状态非常有用,因为它允许其他进程(通常是创建进程的父进程)检查进程的返回代码,并查看刚刚完成的进程是否成功执行
看到没有,可以告诉父进程,子进程是否成功运行。因为我们可以在子进程的代码逻辑里面,各种exit
,return
,父进程就可以通过这个来检测代码运行情况。
至于父进程怎么判断子进程的运行状况,且耐着性子往下看
僵尸进程的解决
父进程需要给子进程“收尸”,可是他们之间似乎从fork之后的就各做各的去了。这时候,就需要提供一些额外手段了。
- wait函数
- waitpid函数
- 信号
SIGCHLD
前两种很好理解,意思是父进程早就知道会出现“白发人送黑发人的情况”。那么就早早地通过这两个函数等待子进程的运行结束。而第三种,表示以信号处理的方式来销毁僵尸进程
wait函数
首先看下wait函数在linux下的定义
#include <sys/wait.h>
pid_t wait(int *status);
// 以阻塞的方式等待一个子进程的终止。
// 返回值:
// 1. 子进程ID
// 2. -1,函数出错
调用此函数时如果已有子进程终止,那么子进程终止时传递的返回值(exit函数的参数值,main函数的return返回值)将保存到该函数的参数所指内存空间。但函数参数指向的单元中还包含其他信息。因此需要通过下列宏进行分离:
-
WIFEXITED
子进程正常终止时返回真 -
WEXITSTATUS
返回子进程的返回值
如果没有已终止的子进程,那么程序将阻塞直到有子进程终止。因此考虑使用waitpid
函数
代码示例
因此,在向wait函数传递变量status的地址时,调用wait函数后应该通过下列形式调用
#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>
#include <signal.h>
void testWait()
{
int status = 0;
wait(&status);
if (WIFEXITED(status))
{
puts("normal termination");
printf("Child pass num: %d\n", WEXITSTATUS(status));
}
else
{
puts("non-normal termination");
printf("Child pass num: %d\n", WEXITSTATUS(status));
}
}
int main(void)
{
pid_t pid;
printf("before fork\n");
pid = fork();
if (pid == 0)
{
printf("entering child process, child pid = %d\n", getpid());
}
else
{
printf("continue parent process, %d\n", getpid());
// wait(&status);
// if (WIFEXITED(status))
// {
// puts("normal termination");
// printf("Child pass num: %d\n", WEXITSTATUS(status));
// }
testWait();
}
return 0;
}
waitpid及其使用
通过信号消灭僵尸进程
写在后面
已经讲清楚了,僵尸进程是什么。以及为什么要有僵尸进程,而不是直接结束以后就销毁。还有父进程怎么去销毁或者获取子进程的运行状态。
但是因为时间关系,就先写个上篇。做个记录。等空了写个下篇。
凡事要认真。