目录

注:

一,啥是僵尸进程?

如何让程序再后台运行?

笔记:

二,僵尸进程的危害:

三,如何避免僵尸进程???

要想得到子进程退出信息,要下面两个方法,就是2)3):

再次声明:

 

注:

        本文章内容均来自本人的个人笔记为个人学习总结,禁止他人转载,参考自B站课程:码农论坛《C++环境高级编程》。由于当时方便记笔记,笔记中部分图片来源于原课程视频截图,版权归原作者“码农论坛”及相关权利人所有。对于linux系统,文章中我用的ubuntu,up主用的centos,但原理是相同的,不影响技术学习。

本笔记无任何商业用途(除开csdn官方操作),仅供个人学习交流。感谢原up主的课程分享!

一,啥是僵尸进程?

        在 Linux 中,僵尸进程(Zombie Process)是指子进程已结束运行,但父进程尚未调用 wait() 或 waitpid() 回收其退出状态,导致该进程在进程表中保留一个条目,状态显示为 <defunct>。它不再占用CPU,但会占用进程表资源,数量过多可能导致系统无法创建新进程。

先看代码:

这样,父进程先退出,编译运行,查看进程状态:

一个是20s内,一个是20s后,父进程已经退出了。

子进程的父进程之前是7301,现在是1,

修改代码:

让改程序再后台运行。

如何让程序再后台运行?

方法一:加上&符号

这样可以运行,但是影响一点观察。

使用kill test命令停下来:

方法2:使用一行语句:

此时,无需&符号。

 

笔记:

如果父进程比子进程先退出,子进程将被1号进程托管(这也是一种让程序在后台运行的方法)。

如果子进程比父进程先退出,而父进程没有处理子进程退出的信息,那么,子进程将成为僵尸进程。

代码:

若是子进程,就直接退出,是父进程,就继续运行

编译运行:

因为父进程没有退出,所以在这个界面上不能输其他命令。

查看进程:

注意,下面这个就是子进程,子进程已经退出了,但是它的信息还保存在系统当中。

而且子进程这里显示了defunct,就是指失效了。

 

这种失效的进程,又叫僵尸进程。

使用top命令:

这里的zombie,就是僵尸进程!

二,僵尸进程的危害:

        内核为每个子进程保留了一个数据结构,包括进程编号、终止状态、使用CPU时间等。父进程如果处理了子进程退出的信息,内核就会释放这个数据结构,父进程如果没有处理子进程退出的信息,内核就不会释放这个数据结构,子进程的进程编号将一直被占用。系统可用的进程编号是有限的,如果产生了大量的僵尸进程,将因为没有可用的进程编号而导致系统不能产生新的进程。

三,如何避免僵尸进程???

1)子进程退出的时候,内核会向父进程发头SIGCHLD信号,如果父进程用signal(SIGCHLD,SIG_IGN)通知内核,表示自己对子进程的退出不感兴趣,那么子进程退出后会立即释放数据结构。

代码:

编译运行:

查看进程:

此时,只有父进程了,没有僵尸进程!

这个方法只能让子进程退出,但是父进程得不到子进程退出的信息

要想得到子进程退出信息,要下面两个方法,就是2)3):

2)父进程通过wait()/waitpid()等函数等待子进程结束,在子进程退出之前,父进程将被阻塞待。

pid_t wait(int *stat_loc);  //用的最多

下面三个功能更强:

pid_t waitpid(pid_t pid, int *stat_loc, int options);

pid_t wait3(int *status, int options, struct rusage *rusage);

pid_t wait4(pid_t pid, int *status, int options, struct rusage *rusage);

返回值是子进程的编号。

stat_loc是子进程终止的信息:a)如果是正常终止,宏WIFEXITED(stat_loc)返回真,宏WEXITSTATUS(stat_loc)可获取终止状态;b)如果是异常终止,宏WTERMSIG(stat_loc)可获取终止进程的信号。

 

sts 是 32 位整数,不同位存储不同信息,系统提供宏来解析,核心宏如下:

表格:

作用

示例值含义

WIFEXITED(sts)

判断子进程是否正常退出(通过 exit()/return

返回 true → 正常退出;false → 异常退出

WEXITSTATUS(sts)

提取正常退出的退出码(仅 WIFEXITED(sts) 为 true 时有效)

0 → 正常结束;1 → 业务逻辑错误等

WTERMSIG(sts)

提取导致异常退出的信号编号(仅 WIFEXITED(sts) 为 false 时有效)

9 → 被 kill -9 终止;11 → 段错误(SIGSEGV)

第一个wait函数,返回子进程编号,传地址。

代码:

#include <iostream>   // C++ 标准输入输出(替代C的printf)
#include <sys/types.h> // 定义 pid_t(进程ID类型)
#include <sys/wait.h>  // 包含 wait() 函数和退出状态解析宏
#include <unistd.h>    // 包含 fork()、sleep() 等系统调用
using namespace std;

int main()
{
    // 1. 创建子进程:fork() 返回值 >0 是父进程,=0 是子进程
    if (fork() > 0)
    { // ------------------------ 父进程分支 ------------------------
        int sts; // 存储子进程的退出状态(核心:是一个32位的状态码,包含退出类型/退出码/信号等信息)
        // 2. 阻塞等待子进程退出:wait(&sts) 会暂停父进程,直到任意子进程结束
        //    返回值:结束的子进程PID;如果出错返回-1
        pid_t pid = wait(&sts);

        // 3. 打印结束的子进程ID
        cout << "已终止的子进程编号是: " << pid << endl;

        // 4. 解析子进程退出状态(核心:通过系统宏解析sts)
        if (WIFEXITED(sts)) {
            // 4.1 子进程「正常退出」(通过 exit()/return 退出)
            // WEXITSTATUS(sts):提取正常退出时的退出码(0-255)
            cout << "子进程是正常退出的,退出状态是: " << WEXITSTATUS(sts) << endl;
        } else {
            // 4.2 子进程「异常退出」(被信号终止,比如段错误、kill -9)
            // WTERMSIG(sts):提取导致子进程终止的信号编号
            cout << "子进程是异常退出的,终止它的信号是: " << WTERMSIG(sts) << endl;
        }
    }
    else
    { // ------------------------ 子进程分支 ------------------------
        sleep(5); // 子进程休眠5秒(模拟业务逻辑执行)
        // 以下两行是「异常退出测试代码」,注释时子进程正常退出,取消注释触发段错误
        // int *p = 0; // 空指针(指向内存地址0)
        // *p = 10;    // 解引用空指针 → 触发段错误(信号SIGSEGV,编号11)
        exit(0);    // 子进程正常退出,退出码为0(等同于 return 0)
    }
    return 0;
}

wait(&sts) —— 父进程等待子进程的核心函数

作用:父进程调用后会阻塞(暂停执行),直到任意一个子进程退出,才会继续执行;

参数 sts:输出型参数,存储子进程的退出状态(不是简单的数字,是包含多种信息的 32 位数据);

返回值:成功返回退出的子进程 PID,失败返回 - 1(比如没有子进程);

为什么需要 wait():如果父进程不调用 wait(),子进程退出后会变成「僵尸进程」(占用进程表资源),wait() 会清理僵尸进程并获取退出状态。

编译运行,在这5s内查看进程:

运行结束,查看结果:

子进程编号,3083,刚好显示子进程退出!

这里子进程是正常退出的,退出状态是0

将退出状态改为1(exit(1))也是:

再来查看异常退出的情况;

编译运行,查看进程,并kill子进程:

再次运行程序,查看进程编号,用-9杀死程序:

注意:每次运行进程编号都不同!!!

接着,启用这行代码:

这里直接操作空指针,产生段错误,信号11

3)如果父进程很忙,可以捕获SIGCHLD信号,在信号处理函数中调用wait()/waitpid()

代码:

父进程是一个死循环,而子进程在运行5s之后异常退出(解引用空指针,出现段错误):

编译运行:

子进程退出后,查看进程:

发现只有父进程,子进程已经退出了。

再次声明:

本文章内容均来自本人的个人笔记为个人学习总结,禁止转载。
参考自B站课程:码农论坛《C++环境高级编程》。本文章保留了原课程的部分代码以及运行结果的截图。

根据《中华人民共和国著作权法》第二十四条规定,本笔记引用上述内容系为个人学习、研究之目的,属于“合理使用”范畴,不影响原作品的正常使用,亦不损害原著作权人的合法权益。

本笔记无任何商业用途,仅供个人学习交流。感谢原up主的课程分享!

 

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐