106.进程控制(结束、孤儿、僵尸进程)以及进程回收

目录

结束进程

孤儿进程 

僵尸进程 

进程回收 

wait()

waitpid


        进程控制是指在操作系统中对进程进行创建、终止、挂起、唤醒以及进程之间的同步、通信等操作的管理。

结束进程

exit()_exit() 函数都用于终止一个进程,但它们之间有一些重要的区别:

exit() 函数:

  • 头文件: #include <stdlib.h>
  • 功能: exit() 函数用于正常终止程序的执行。它执行一系列清理操作,包括调用通过 atexit() 注册的终止处理程序,并将程序的返回状态传递给操作系统。
  • 清理操作: 会调用通过 atexit() 注册的终止处理程序,关闭文件流(通过 fclose()),刷新缓冲区等。
  • 使用范例:
    #include <stdlib.h>int main() {// 一些代码// 正常退出,返回状态码 0exit(0);
    }
    

    _exit() 函数:

  • 头文件: #include <unistd.h>
  • 功能: _exit() 函数也用于终止程序,但它是一个较低级别的函数。它不会执行 exit() 做的清理工作,包括不会调用通过 atexit() 注册的函数,不会刷新 I/O 缓冲区等。
  • 适用场景: _exit() 主要用于在子进程中立即终止程序而无需执行清理操作。在这种情况下,使用 _exit() 可以避免执行 exit() 中的一些不必要的操作。
  • 使用范例:
    #include <unistd.h>int main() {// 一些代码// 立即退出,不执行清理操作_exit(0);
    }
    

    在大多数情况下,如果你需要正常终止程序并执行清理操作,应该使用 exit() 函数。如果你希望在子进程中立即退出,可以使用 _exit()

孤儿进程 

        孤儿进程是指在其父进程结束或者被终止后,仍然在系统中运行的子进程。孤儿进程会被操作系统的 init 进程(进程号为1)接管,并由 init 进程负责回收。这确保了孤儿进程不会成为系统资源的泄漏。

        当一个进程创建了子进程,而父进程先于子进程结束,那么子进程就会变成孤儿进程。这通常发生在父进程创建子进程后,父进程先于子进程调用 exit() 终止,或者父进程意外崩溃的情况。

        init 进程会定期检查是否有孤儿进程,如果发现孤儿进程,就会成为孤儿进程的新的父进程,并负责回收它的资源。这种处理方式确保了操作系统的稳定性和资源管理的有效性。

下面是一个产生孤儿进程的简单示例:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>int main()
{pid_t child_pid = fork();if (child_pid < 0){// 错误处理perror("fork");exit(0);}if (child_pid > 0){// 父进程printf("我是父进程:pid=%d\n", getpid());exit(0);}else if (child_pid == 0){// 子进程sleep(2);printf("我是子进程:pid=%d,父进程:ID=%d\n", getpid(), getppid());}return 0;
}

        在 Linux 中,如果一个进程的父进程终止,而它还没有被领养,那么它会被 init 进程(PID=1)领养。这确保了孤儿进程总是有一个父进程。init 进程在系统启动时由内核启动,是所有进程的祖先。

        对于没有桌面环境的系统,确实是 init 进程接管孤儿进程。但是,对于有桌面环境的系统,具体情况可能有所不同,因为桌面环境通常有自己的进程管理机制。

僵尸进程 

        僵尸进程是已经结束执行的进程,但其在进程表中仍然保留着一定的信息,包括进程号(PID)和退出状态等。这样的进程称为僵尸进程。僵尸进程的存在可能导致系统中的进程表被占用,过多的僵尸进程可能影响系统的正常运行。

        通常,一个进程在结束时会向其父进程发送一个信号,告诉父进程它已经结束。父进程接收到这个信号后,应该调用 waitwaitpid 系统调用来获取子进程的退出状态,释放子进程的资源。如果父进程没有及时处理,子进程就会变成僵尸进程。

运行下面的代码就可以得到一个僵尸进程了:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>int main()
{pid_t pid;// 创建子进程for (int i = 0; i < 5; ++i){pid = fork();if (pid == 0){break;}}// 父进程if (pid > 0){// 需要保证父进程一直在运行// 一直运行不退出, 并且也做回收, 就会出现僵尸进程while (1){printf("我是父进程, pid=%d\n", getpid());sleep(1);}}else if (pid == 0){// 子进程, 执行这句代码之后, 子进程退出了printf("我是子进程, pid=%d, 父进程ID: %d\n", getpid(), getppid());}return 0;
}

        这段代码创建了一个父进程和5个子进程。子进程在输出一行信息后立即退出,而父进程则进入一个无限循环,在每次循环中输出一行信息。

        在这个场景中,由于父进程一直在运行,而子进程在输出信息后就退出了,子进程就有可能成为僵尸进程。父进程没有调用 waitwaitpid 函数来回收子进程,因此子进程退出后,其退出状态信息会一直保留在进程表中,形成僵尸进程。

进程回收 

        为了避免僵尸进程的产生,一般我们会在父进程中进行子进程的资源回收,回收方式有两种,一种是阻塞方式wait(),一种是非阻塞方式waitpid()。

wait()

        这是个阻塞函数,如果没有子进程退出, 函数会一直阻塞等待, 当检测到子进程退出了, 该函数阻塞解除回收子进程资源。这个函数被调用一次, 只能回收一个子进程的资源,如果有多个子进程需要资源回收, 函数需要被调用多次。

函数原型如下:

#include <sys/types.h>
#include <sys/wait.h>pid_t wait(int *status);

返回值:

  • 成功:返回被回收的子进程的进程ID
  • 失败: -1
  • 没有子进程资源可以回收了, 函数的阻塞会自动解除, 返回-1
  • 回收子进程资源的时候出现了异常
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>// wait 函数回收子进程资源
int main()
{pid_t pid;// 创建子进程for (int i = 0; i < 5; ++i){pid = fork();if (pid == 0){break;}}// 父进程if (pid > 0){// 需要保证父进程一直在运行while (1){// 回收子进程的资源// 子进程由多个, 需要循环回收子进程资源pid_t ret = wait(NULL);if (ret > 0){printf("成功回收了子进程资源, 子进程PID: %d\n", ret);}else{printf("回收失败, 或者是已经没有子进程了...\n");break;}printf("我是父进程, pid=%d\n", getpid());}}else if (pid == 0){// 子进程, 执行这句代码之后, 子进程退出了printf("我是子进程, pid=%d, 父进程ID: %d\n", getpid(), getppid());}return 0;
}

waitpid

        waitpid() 函数可以看做是 wait() 函数的升级版,通过该函数可以控制回收子进程资源的方式是阻塞还是非阻塞,另外还可以通过该函数进行精准打击,可以精确指定回收某个或者某一类或者是全部子进程资源。

#include <sys/wait.h>pid_t waitpid(pid_t pid, int *status, int options);
  • pid 参数指定要等待的子进程:

    • 如果 pid > 0,则等待进程ID等于 pid 的子进程。
    • 如果 pid == 0,则等待与调用进程在同一进程组的任一子进程。
    • 如果 pid == -1,则等待任一子进程,与 wait 效果相同。
    • 如果 pid < -1,则等待进程组ID等于 pid 绝对值的任一子进程。
  • status 是一个指向整型的指针,用于保存子进程的退出状态。

  • options 是一组位掩码,可以通过按位或运算来组合。常用的选项有:

    • WNOHANG:以非阻塞方式等待,即使没有子进程退出也立即返回。

返回值:

  • 如果函数是非阻塞的, 并且子进程还在运行, 返回0
  • 成功: 得到子进程的进程ID
  • 失败: -1
  • 没有子进程资源可以回收了, 函数如果是阻塞的, 阻塞会解除, 直接返回-1
  • 回收子进程资源的时候出现了异常
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>// 和wait() 行为一样, 阻塞
int main()
{pid_t pid;// 创建子进程for (int i = 0; i < 5; ++i){pid = fork();if (pid == 0){break;}}// 父进程if (pid > 0){// 需要保证父进程一直在运行while (1){// 回收子进程的资源// 子进程由多个, 需要循环回收子进程资源int status;pid_t ret = waitpid(-1, &status, 0); // == wait(NULL);if (ret > 0){printf("成功回收了子进程资源, 子进程PID: %d\n", ret);// 判断进程是不是正常退出if (WIFEXITED(status)){printf("子进程退出时候的状态码: %d\n", WEXITSTATUS(status));}if (WIFSIGNALED(status)){printf("子进程是被这个信号杀死的: %d\n", WTERMSIG(status));}}else{printf("回收失败, 或者是已经没有子进程了...\n");break;}printf("我是父进程, pid=%d\n", getpid());}}else if (pid == 0){// 子进程, 执行这句代码之后, 子进程退出了printf("===我是子进程, pid=%d, 父进程ID: %d\n", getpid(), getppid());}return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/258575.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

什么是JVM的内存模型?详细阐述Java中局部变量、常量、类名等信息在JVM中的存储位置

导航&#xff1a; 【Java笔记踩坑汇总】Java基础JavaWebSSMSpringBootSpringCloud瑞吉外卖/黑马旅游/谷粒商城/学成在线设计模式面试题汇总性能调优/架构设计源码-CSDN博客 目录 一、JVM基本介绍 二、JVM内存模型 2.0 概述 2.1 类加载子系统 2.2 运行时数据区 2.2.0 基本…

时域频域(学习记录1)

1 小伙伴们&#xff0c;今天让我们一起来聊聊Something about DATA 系列。我们先回顾一下本系列对NVH测试中的数据采集做的整体介绍&#xff1a; A 数据采集过程&#xff1b; B 硬件设备&#xff1b; C 数采软件&#xff1b; D ATOM中的数据采集&#xff1b; 接下来的几篇文章…

小程序一键生成工具哪个好?

在这个数字化时代&#xff0c;小程序已经成为商家吸引客户、提升业务的重要工具。但是&#xff0c;传统的小程序开发方式既费时又费力&#xff0c;让许多商家望而却步。 现在&#xff0c;有了乔拓云小程序模板开发平台&#xff0c;一切都变了。 乔拓云提供了大量精心设计的模板…

5G基站行业节能降耗 解决方案

截至2023年10月&#xff0c;我国5G基站总数达321.5万个&#xff0c;占全国通信基站总数的28.1%。然而&#xff0c;随着5G基站数量的快速增长&#xff0c;基站的能耗问题也逐渐日益凸显&#xff0c;基站的用电给运营商带来了巨大的电费开支压力&#xff0c;降低5G基站的能耗成为…

flstudio21破解汉化版2024最新水果编曲使用教程

​ 如果你一直梦想制作自己的音乐(无论是作为一名制作人还是艺术家)&#xff0c;你可能会想你出生在这个时代是你的幸运星。这个水果圈工作室和上一版之间的改进水平确实令人钦佩。这仅仅是FL Studio 21所提供的皮毛。你的音乐项目的选择真的会让你大吃一惊。你以前从未有过这…

JS原生实现浏览器滚动条滚动侧边栏高亮响应

目录 演示 ​编辑 需求 代码 css html script 代码解释 1、获取所有link-content 2、定义一个rectContent数组&#xff0c;然后循环allContents调用getClientRects()[0]获取每个link-content元素与浏览器视口的关系 3、为数组追加link-content&#xff0c;用于设置侧…

《Spring Cloud Alibaba 从入门到实战》理论知识篇

基础知识篇 理论篇 俗话说&#xff0c;没有最好的架构&#xff0c;只有最合适的架构。 微服务架构也是随着信息产业的发展而出现的最有普遍适用性的一套架构模式。 通常来说&#xff0c;我们认为架构发展历史经历了这样一个过程&#xff1a;单体架构 -> SOA 面向服务架构 …

软件设计师——面向对象技术(一)

&#x1f4d1;前言 本文主要是【面向对象技术】——软件设计师—面向对象技术的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是听风与他&#x1f947; ☁️博客首页&#xff1a;CSDN主页听风与他 &#…

C++: 多态

多态的基本概念&#xff1a; 多态是 C 面向对象三大特性之一 多态分为两类&#xff1a; 静态多态 : 函数重载 和 运算符重载属于静态多态&#xff0c;复用函数名 动态多态 : 派生类和虚函数实现运行时多态 静态多态和动态多态区别&#xff1a; 静态多态的函数地址早绑定 …

高德地图画渐变线

高德地图画渐变线&#xff0c;思路是将线和颜色均分为多个小线段和小颜色&#xff0c;实现渐变&#xff0c;类似于下图。 如果需要多段线&#xff0c;自己循环拼一下就可以了&#xff0c;方法返回多个小线段组成的polyline数组。 /** 高德地图画渐变线* author: liyun* params…

第一课【习题】HarmonyOS应用/元服务上架

元服务发布的国家与地区仅限于“中国大陆” 编译打包的软件包存放在项目目录build > outputs > default下 创建应用时&#xff0c;应用包名需要和app.json5或者config.json文件中哪个字段保持一致&#xff1f; 发布应用时需要创建证书&#xff0c;证书类型选择什么…

粤能环保亮相迪拜COP28,智能技术铸就运河城市可持续未来

在全球应对气候变化的重要会议——迪拜COP28大会上&#xff0c;运河城市面临的独特环境挑战引起了广泛关注。随着城市化进程的加快&#xff0c;运河城市在处理固体废物、减少温室气体排放以及维持水资源安全方面面临着严峻考验。智能垃圾分类作为应对这些挑战的有效途径&#x…