当前位置: 首页 > 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;
}

        输出结果如下:

相关文章:

  • Spring Cloud Hystrix熔断机制:构建高可用微服务的利器
  • 今天对C语言中static和extern关键字的作用认识又深刻了
  • 174页PPT家居制造业集团战略规划和运营管控规划方案
  • SQLMesh实战:用虚拟数据环境和自动化测试重新定义数据工程
  • 高频 PCB 技术发展趋势与应用解析
  • Python 基础核心语法:输入输出、变量、注释与字符串操作
  • 数据通信与计算机网络——数字传输
  • python打卡第45天
  • electron-vite串口通信
  • go中的接口返回设计思想
  • 限流算法java实现
  • conda环境配置(一) —— 常用虚拟环境操作命令
  • 多文化软件团队的协作之道:在认知差异中寻找协同的支点
  • 网络原理5 - TCP4
  • N元语言模型 —— 一文讲懂!!!
  • Fetch与Axios:区别、联系、优缺点及使用差异
  • 6个月Python学习计划 Day 16 - 面向对象编程(OOP)基础
  • 【GESP真题解析】第 15 集 GESP 三级 2024 年 9 月编程题 2:回文拼接
  • 自适应长度惩罚强化学习的高效推理
  • 基于TarNet、CFRNet与DragonNet的深度因果推断模型全解析
  • 做ppt设计师哪个网站好/2021年年度关键词
  • js网站变灰色代码/站长seo综合查询
  • 1元做网站方案/哪个公司的网站制作
  • 南充网站建设/qq群推广网站免费
  • 移动网站建设哪家快/seo1域名查询
  • 网站建设论文选题背景/郑州seo外包顾问