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

news2024/11/18 17:22:35

目录

结束进程

孤儿进程 

僵尸进程 

进程回收 

wait()

waitpid


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

结束进程

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

exit() 函数:

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

OpenAI的Sam Altman,获《时代》2023年度最佳CEO

12月7日&#xff0c;《时代》周刊在官网公布了2023年最佳CEO——OpenAI的Sam Altman。 此外&#xff0c;梅西入选了年度最佳运动员&#xff0c;Taylor Swift入选年度最佳人物&#xff0c;Alex Newell获年度突破奖。 《时代》周刊曾在今年的9月8日发布了“2023年AI领域最有影响…

Stable Diffusion XL on diffusers

Stable Diffusion XL on diffusers 翻译自&#xff1a;https://huggingface.co/docs/diffusers/using-diffusers/sdxl v0.24.0 非逐字翻译 Stable Diffusion XL (SDXL) 是一个强大的图像生成模型&#xff0c;其在上一代 Stable Diffusion 的基础上主要做了如下优化&#xff1a;…

超详细介绍Ubuntu系统安装CUDA和cuDNN【一站式服务!!!】

文章目录 简介1.安装显卡驱动查看显卡型号下载并安装NVIDIA驱动使用Ubuntu自带的软件和更新&#xff08;Software&Updates&#xff09;工具安装【博主使用的这种方式&#xff0c;推荐】自行下载使用命令行安装【自由度更高&#xff0c;大佬自行尝试】 2.下载并安装CUDA3.下…

docker容器_自定义上传jenkins镜像(Dockerfile实现)

1.创建jenkins目录&#xff0c;并上传相应的包 mkdir /jenkins/ 2.创建一个Dockerfile文件 FROM daocloud.io/library/centos:7#把当前目录下的jenkins.war包传到内部容器的/ 下 ADD ./jenkins.war /#把当前目录下的jdk传到内部容器的/opt/,并解压 ADD ./jdk-11.0.19_linu…

【软件推荐】文本转语音,语音转wav,导入ue5

文字转语音 在线免费文字转语音 - TTSMaker官网 | 马克配音https://ttsmaker.cn/ 文件转换器 语音转wav Convertio — 文件转换器https://convertio.co/zh/

前端学习微信小程序开发

1.微信小程序项目结构 2.WXML和HTML的区别 3.WXSS与CSS的区别 4.小程序中的.js文件 5.小程序的宿主环境 宿主环境是指程序运行所必须的依赖环境&#xff0c;因此手机微信时小程序的宿主环境。小程序宿主环境包含了通信模型、运行机制、组件、API。 &#xff08;1&#xff09;…

基于jsp+servlet的图书管理系统

基于jspservlet的图书管理系统演示地址为 图书馆后台管理系统 用户名:mr ,密码:123 图书馆管理系统主要的目的是实现图书馆的信息化管理。图书馆的主要业务就是新书的借阅和归还&#xff0c; 因此系统最核心的功能便是实现图书的借阅和归还。此外&#xff0c;还需要提供图书…

小视频怎么做成二维码?视频二维码3步生成

在日常工作和生活中经常会看到各种类型的小视频、短视频&#xff0c;比如网页、抖音等等的视频都是可以下载查看的。当我们想要将下载视频分享给多个人看时&#xff0c;生成二维码的方式会更加的方便&#xff0c;那么视频如何生成二维码呢&#xff1f;下面就将快捷生成二维码的…

基于SpringBoot+Vue学生成绩管理系统前后端分离(源码+数据库)

一、项目简介 本项目是一套基于SpringBootVue学生成绩管理系统&#xff0c;主要针对计算机相关专业的正在做bishe的学生和需要项目实战练习的Java学习者。 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目可以直接作为bishe使用。 项目都经过严格调试&#xff0c;确…

【送书活动四期】被GitHub 要求强制开启 2FA 双重身份验证,我该怎么办?

记得是因为fork了OpenZeppelin/openzeppelin-contracts的项目&#xff0c;之后就被GitHub 要求强制开启 2FA 双重身份验证了&#xff0c;一拖再拖&#xff0c;再过几天帐户操作将受到限制了&#xff0c;只能去搞一下了 目录 2FA是什么为什么要开启 2FA 验证GitHub 欲在整个平台…

【每日一题】重新规划路线

文章目录 Tag题目来源题目解读解题思路方法一&#xff1a;深度优先搜索方法二&#xff1a;广度优先搜索 写在最后 Tag 【深搜】【广搜】【树】【2023-12-07】 题目来源 1466. 重新规划路线 题目解读 题目给定一张由 n个点&#xff08;使用 0 到 n−1 编号&#xff09;&#…

优秀案例 | 元宇宙双语财经科技主播“舒望”主持首届粤港澳大湾区元宇宙国际传播论坛

12月6日&#xff0c;由南方财经全媒体集团指导、大湾区元宇宙国际传播实验室(GBA MIC Lab&#xff09;主办、南财国际传播中心和21世纪经济报道共同承办&#xff0c;以“多元共创开放共享”为主题的首届粤港澳大湾区元宇宙国际传播论坛在广州隆重开幕。 “立足湾区&#xff0c;…

当然热门的原创改写改写大全【2023最新】

在信息时代&#xff0c;随着科技的不断发展&#xff0c;改写软件逐渐成为提高文案质量和写作效率的重要工具。本文将专心分享一些好用的改写软件&#xff0c;其中包括百度文心一言智能写作以及147SEO改写软件。这些工具不仅支持批量改写&#xff0c;而且在发布到各大平台后能够…

“福利”还是“陷阱”?公司给员工放假3个月引发劳动权益争议

近日&#xff0c;广东佛山一家玻璃制造公司的长达3个月放假通知引发广泛关注。这一决策引发了社会对员工福利和公司经营平衡的深入思考。公司表示&#xff0c;此次决策是为了维修老化设备&#xff0c;但随之而来的疑虑则主要集中在员工的收入和劳动权益问题上。 公司表示&…

SQL进阶 | CASE表达式

本文所有案例基于《SQL进阶教程》实现。 概述 SQL中的CASE表达式是一种通用的条件表达式&#xff0c;类似于其他语言中的if/else语句。它用于在SQL语句中实现条件逻辑。CASE表达式以WHEN子句开始&#xff0c;后面跟着一个或多个WHEN条件&#xff0c;每个WHEN条件后面跟着一个TH…

实战:Docker Compose 下 Nginx、Java、Mysql 和 Redis 服务协同部署(包含解决浏览器访问Linux部署服务器本地资源问题)

1. 背景 在该实战中&#xff0c;我们将探讨如何使用Docker Compose协同部署Nginx、Java、Mysql和Redis服务&#xff0c;实现一个视频上传与展示的应用。具体需求如下&#xff1a; Java应用负责上传视频和图片资源到Nginx目录下&#xff0c;作为资源服务器。Nginx服务作为静态…

C++相关闲碎记录(5)

1、容器提供的类型 2、Array Array大小固定&#xff0c;只允许替换元素的值&#xff0c;不能增加或者移除元素改变大小。Array是一种有序集合&#xff0c;支持随机访问。 std::array<int, 4> x; //elements of x have undefined value std::array<int, 5> x {…

cpu 300% 爆满 内存占用不高 排查

top查询 cpu最高的PID ps -ef | grep PID 查看具体哪一个jar服务 jstack -l PID > ./jstack.log 下载/打印进程的线程栈信息 可以加信息简单分析 或进一步 查看堆内存使用情况 jmap -heap Java进程id jstack.log 信息示例 Full thread dump Java HotSpot(TM) 64-Bit Se…

陪诊软件开发|北京陪诊系统功能详解

在这个快节奏的生活中&#xff0c;寻求医疗服务往往让人感到繁琐和时间浪费。然而&#xff0c;现如今&#xff0c;随着科技的不断进步&#xff0c;一项创新的上门服务系统正在改变传统的医疗体验&#xff0c;带来了前所未有的便利和舒适。 陪诊系统功能&#xff1a; 1、诊前约…

海上液化天然气 LNG 终端 | 图扑数字孪生

关于 LNG 液化天然气 (Liquefied Natural Gas&#xff0c;简称 LNG) 在能源转型过程中被广泛认可为相对较清洁的能源选择。 相对于传统的煤炭和石油燃料&#xff0c;LNG 的燃烧过程产生的二氧化碳 (CO2) 排放较低。LNG 的燃烧释放的二氧化碳排放较少&#xff0c;因此对应对气…