当前位置: 首页 > news >正文

僵尸进程是什么?怎么回收?孤儿进程?

僵尸进程是什么?

  1. 僵尸进程的定义:对于多进程程序,当子进程结束运行但父进程还未读取其退出状态时,子进程就处于僵尸态。此时,内核不会立即释放该子进程的进程表表项,以满足父进程后续查询子进程退出信息的需求
  2. 产生原因:子进程运行结束后,父进程若没有及时获取其退出状态,子进程就会一直处于僵尸态;当父进程提前结束或异常终止,而子进程继续运行时,子进程的 PPID(父进程 ID)会被操作系统设置为 1,即由 init 进程接管。在父进程退出后到子进程退出前这段时间,子进程也处于僵尸态。
  3. 僵尸进程的危害:僵尸进程会一直占据内核资源,而内核资源是有限的。如果大量产生僵尸进程,可能会导致系统资源耗尽,影响系统性能。

处理僵尸进程的函数

        pid_t wait(int *stat_loc)

        wait 函数会阻塞调用它的进程(通常是父进程),直到该进程的任意一个子进程结束运行。这意味着调用 wait 后,父进程会暂停执行,等待子进程完成任务。
        当有子进程结束时,wait 函数返回结束运行的子进程的进程 ID(PID),同时将该子进程的退出状态信息存储在 stat_loc 参数指向的内存位置。退出状态信息包含了子进程是如何结束的,例如是正常退出还是因信号终止等。
        通过 sys/wait.h 头文件中定义的宏来解析 stat_loc 中的退出状态信息:

  • WIFEXITED(stat_val):用于判断子进程是否正常结束。如果子进程正常结束,该宏返回一个非零值(即真)。
  • WEXITSTATUS(stat_val):当 WIFEXITED 返回非零值时,使用此宏可以获取子进程的退出码。子进程通过 exit 函数或从 main 函数返回时设置的退出码,可以通过这个宏获取。
  • WIFSIGNALED(stat_val):如果子进程是因为一个未捕获的信号而终止,此宏返回一个非零值。
  • WTERMSIG(stat_val):当 WIFSIGNALED 返回非零值时,该宏返回导致子进程终止的信号值。例如,如果子进程因接收到 SIGTERM 信号而终止,WTERMSIG 将返回 SIGTERM 的值。
  • WIFSTOPPED(stat_val):若子进程被暂停(例如收到 SIGSTOP 信号),此宏返回一个非零值。
  • WSTOPSIG(stat_val):当 WIFSTOPPED 返回非零值时,该宏返回导致子进程暂停的信号值。

        示例代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>int main() {pid_t pid;int status;pid = fork();if (pid == -1) {perror("fork");return 1;} else if (pid == 0) {// 子进程printf("Child process: My PID is %d\n", (int)getpid());exit(10); // 子进程正常退出,退出码为10} else {// 父进程pid_t terminated_pid = wait(&status);if (terminated_pid == -1) {perror("wait");return 1;}if (WIFEXITED(status)) {printf("Child %d exited normally with exit code %d\n", (int)terminated_pid, WEXITSTATUS(status));} else if (WIFSIGNALED(status)) {printf("Child %d terminated by signal %d\n", (int)terminated_pid, WTERMSIG(status));}}return 0;
}

        输出如下:

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

   waitpid 函数提供了比 wait 函数更灵活的等待方式。它可以等待由 pid 参数指定的特定子进程。

  • pid > 0:等待进程 ID 为 pid 的子进程。
  • pid = -1:等待任意一个子进程,此时 waitpid 的行为和 wait 函数相同。
  • pid = 0:等待与调用进程同组的任意子进程。
  • pid < -1:等待进程组 ID 等于 pid 绝对值的任意子进程。

        与 wait 函数类似,waitpid 函数返回结束运行的子进程的 PID,并将子进程的退出状态信息存储在 stat_loc 参数指向的内存位置,同样可以使用 sys/wait.h 中的宏来解析退出状态。
        options 参数可以控制 waitpid 函数的行为。最常用的取值是 WNOHANG,当 options 取值为 WNOHANG 时,waitpid 调用将是非阻塞的。如果 pid 指定的目标子进程还没有结束或意外终止,则 waitpid 立即返回 0;如果目标子进程确实正常退出了,则 waitpid 返回该子进程的 PID。若 waitpid 调用失败,返回 -1 并设置 errno

   示例代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>int main() {pid_t pid;int status;pid = fork();if (pid == -1) {perror("fork");return 1;} else if (pid == 0) {// 子进程printf("Child process: My PID is %d\n", (int)getpid());sleep(2); // 模拟子进程执行一些任务exit(10); // 子进程正常退出,退出码为10} else {// 父进程while (1) {pid_t terminated_pid = waitpid(pid, &status, WNOHANG);if (terminated_pid == -1) {perror("waitpid");return 1;} else if (terminated_pid == 0) {// 子进程还未结束printf("Child is still running...\n");sleep(1);//模拟父进程处理自己的任务} else {if (WIFEXITED(status)) {printf("Child %d exited normally with exit code %d\n", (int)terminated_pid, WEXITSTATUS(status));} else if (WIFSIGNALED(status)) {printf("Child %d terminated by signal %d\n", (int)terminated_pid, WTERMSIG(status));}break;}}}return 0;
}

        输出如下:
 

        讲解一下示例代码:采用的是轮询的方式检测子进程是否已经退出,如果未退出waitpid()会返回0,进行下次轮检测;如果子进程退出了,会打印子进程的退出码。这样做的好处是,父进程不必阻塞等待子进程退出,它可以边等待边处理自己的任务。

处理僵尸进程更好的方式:利用SIGCHLD信号

        不断的轮询子进程的状态,绝非明智之举。要在事件(当然了这里的事件就是子进程退出)已经发生的情况下,执行非阻塞调用才能提高程序的效率。
        当一个进程结束时,它会给其父进程发送一个SIGCHLD信号。因此,父进程可以捕获这个信号,并在信号处理函数中调用waitpid函数来处理结束的子进程,从而避免僵尸进程的产生。也就是说:SIGCHLD信号触发,相当于通知父进程,你的子进程已经退出了。

   示例代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>// SIGCHLD信号处理函数
static void handle_child(int sig) {pid_t pid;int stat;// 使用waitpid循环处理已结束的子进程while ((pid = waitpid(-1, &stat, WNOHANG)) > 0) {if (WIFEXITED(stat)) {printf("Child %d exited normally with exit code %d\n", (int)pid, WEXITSTATUS(stat));} else if (WIFSIGNALED(stat)) {printf("Child %d terminated by signal %d\n", (int)pid, WTERMSIG(stat));}}
}int main() {pid_t pid;// 注册SIGCHLD信号处理函数struct sigaction sa;sa.sa_handler = handle_child;sigemptyset(&sa.sa_mask);sa.sa_flags = SA_RESTART;if (sigaction(SIGCHLD, &sa, NULL) == -1) {perror("sigaction");exit(1);}// 创建子进程pid = fork();if (pid == -1) {perror("fork");exit(1);} else if (pid == 0) {// 子进程printf("Child process: My PID is %d\n", (int)getpid());sleep(2); // 模拟子进程执行一些任务exit(10); // 子进程正常退出,退出码为10} else {// 父进程printf("Parent process: My PID is %d, Child PID is %d\n", (int)getpid(), (int)pid);// 父进程可以继续执行其他任务while (1) {printf("Parent is doing other things...\n");sleep(1);}}return 0;
}

        输出结果如下:

        对于初学者来说看这段代码有些吃力,建议大家在具备了进程信号的知识的前提下再进行阅读。

什么是孤儿进程?

        孤儿进程指的是父进程在子进程之前终止,导致子进程失去了父进程的管理和控制。此时,这些子进程会被 init 进程(进程 ID 为 1)收养。init 进程是 Linux 系统启动后创建的第一个用户态进程,它负责管理系统中所有孤儿进程的生命周期。
        如果父进程执行完毕或因异常情况提前终止,而它创建的子进程还在运行,这些子进程就会成为孤儿进程。
        写段代码举个例子:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>int main() {pid_t pid = fork();if (pid == -1) {perror("fork");return 1;} else if (pid == 0) {// 子进程printf("Child process: My PID is %d, My PPID is %d\n", (int)getpid(), (int)getppid());sleep(5); // 模拟子进程执行任务printf("Child process: After sleeping, My PPID is %d\n", (int)getppid());} else {// 父进程printf("Parent process: My PID is %d, Child PID is %d\n", (int)getpid(), (int)pid);// 父进程提前结束return 0;}return 0;
}

        输出结果如下:

http://www.lqws.cn/news/211627.html

相关文章:

  • 【Elasticsearch】映射:Join 类型、Flattened 类型、多表关联设计
  • IDEA中的debug使用技巧
  • 代码注释类型
  • 0基础破解Typora,使用正版已激活Typora
  • 柯尼卡美能达Konica Minolta bizhub 205i打印机信息
  • 【20250607接单】Spark + Scala + IntelliJ 项目的开发环境配置从零教学
  • 红队实战全流程:从外部侦察到域控征服的内网渗透 >>> 检测一下小伙伴自己的道行哟
  • 【Java学习笔记】String类(重点)
  • 数论总结,(模版与题解)
  • [特殊字符] 深入理解 Linux 内核进程管理:架构、核心函数与调度机制
  • JAVA理论第四战-线程池
  • 动态可写的四层路由利器ngx_stream_keyval_module
  • WSF07N10 MOSFET 在铲皮机中的应用
  • 【西门子杯工业嵌入式-3-如何使用KEY】
  • 垃圾回收相关八股
  • Redis内存淘汰策略
  • 【Linux】LInux下第一个程序:进度条
  • 东芝Toshiba e-STUDIO2110AC打印机信息
  • 数据结构算法(C语言)
  • Ctrl-Crash 助力交通安全:可控生成逼真车祸视频,防患于未然
  • 基于 Transformer robert的情感分类任务实践总结之二——R-Drop
  • altium designer2024绘制stm32过程笔记x`
  • WebRTC通话原理与入门难度实战指南
  • 从零开始制作小程序简单概述
  • Oracle 审计参数:AUDIT_TRAIL 和 AUDIT_SYS_OPERATIONS
  • SDC命令详解:使用set_port_fanout_number命令进行约束
  • AIGC的产品设计演进:从工具到协作者
  • 生成模型从自回归到变分自动编码器
  • 提供MD5解密的网站
  • WaytoAGI东京大会开启AI全球化新对话:技术无国界,合作促创新