Linux | 进程终止与进程等待

目录

前言

一、进程终止

1、进程终止的几种可能 

2、exit 与 _exit

二、进程等待

1、为什么要进程等待

2、如何进行进程等待

(1)wait函数 

(2)waitpid函数

3、再次深刻理解进程等待


前言

        我们前面介绍进程时说子进程退出,父进程不对子进程进行资源回收,子进程会进入僵尸状态,对于操作系统来说,这是一种资源泄漏,而且还是操作系统层面的资源泄漏,除非父进程退出,否则子进程将一直处于僵尸状态,本章就介绍父进程如何回收子进程;

一、进程终止

1、进程终止的几种可能 

        在介绍回收子进程之前,我们必须对进程终止有一定的了解;所谓进程终止,就是进程的退出,这里我们首先要直到进程退出有以下三种可能;

1、程序正常运行结束,结果正确;

2、程序正常运行结束,结果不正确;

3、程序崩溃,结果不重要;

        可能我们之前对进程退出并没有什么概念,我们是如何区分以上三种情况呢?比如我们以前在virtual studio上运行我们的C/C++程序,我们总会写一个main函数,而一般我们都会在main函数的最后写一个return 0;实际上,这个0就是退出码,我们通过这个判定结果是否正确,退出码有对应的解释含义,我们可以通过 strerror 函数将退出码含义打印出来;这是区分情况一和情况二的方法,对于情况三,程序崩溃,我们的软件 virtual studio 一般会出来一个弹窗,告诉你是哪里引发了程序的崩溃,最常见的就是除零错误、空指针解引用等等,都会导致程序崩溃;下面我们来打印退出码;如下代码;

        我们编译运行上述程序,结果如下;

        我们发现错误码的信息最多编辑到了133号,前面几项我们也很熟悉,其中第一项0就是成功且结果正确;

2、exit 与 _exit

        前面我们说过,在main函数中,我们可以通过return语句让进程退出,并返回返回值;那么要是我们不在main函数呢?那么我们难道要返回main函数再调用return语句?那也太麻烦了吧,实际上,我们也可以通过exit函数和 _ exit函数来使进程终止;如下代码;

        我们编译代码,结果如下所示;这里介绍一条命令 echo $?;可以查看最近运行的一个程序的返回值,我们发现我们输入除-1以外的值时,返回值为0,也就是main函数中的return 语句,而我们输入-1时,返回值为14,是我们调用exit函数的返回值;

        上述的退出函数exit换成_exit也可以实现相同功能,那么其区别在哪呢?看如下代码;

        当我们使用exit函数后,运行结果如下;

        当我们使用_exit函数后,运行结果如下;

        我们已经看不到you can see me;这是因为我们的打印的内容还在C语言的缓冲区内,而我们的_exit是系统调用,在退出前并不能刷新缓冲区;而我们的exit为C语言库函数,会刷新我们的缓冲区;

补充:C语言的缓冲区刷新机制为行刷新,因为在打印时没有换行符,所以我们的使用exit函数时会打印,而使用_exit函数时不会打印;

二、进程等待

1、为什么要进程等待

        其一,这个原因早在我们前面就已经进行了阐述,当子进程退出时,父进程不对子进程进行资源回收,子进程将一直处于僵尸状态,而这种状态会造成系统资源泄漏,这时我们需要通过进程等待的方式,给子进程 “收尸” ;

        其二,我们让子进程去完成任务是否需要子进程完成的如何?对于某些时候,就有这样的需求,因此进程等待另一作用是获取子进程任务完成情况;

2、如何进行进程等待

(1)wait函数 

        关于如何进行进程等待,我们通常是通过系统调用wait和waitpid来实现;我们首先看啊可能wait的函数声明;

        这个函数只有一个参数,是一个输出型参数,所谓输出型参数就是我们传一个指针,函数内会给这个指针执行的值进行赋值返还给我们;这个输出型参数就是子进程的退出码和终止信号,这里我们暂时设置为NULL,等待会介绍waitpid时再做介绍;

        该函数的返回值,若调用成功,返回子进程pid,若失败返回-1.错误码被设置,我们写出如下代码;查看父进程是否回收了子进程;

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>int main()
{int id = fork();if(id == -1){// fork调用失败exit(1);}else if(id == 0){// 子进程int cnt = 5;while(cnt--){printf("我是子进程,我的pid:%d,ppid:%d\n", getpid(), getppid());sleep(1);}exit(14); // 退出码}else  {// 父进程sleep(7);wait(NULL); // 进程等待sleep(2);}return 0;
}

        我们再在命令行输入以下脚本命令对这两个进程进行监控;

while :; do ps -axj | head -1 && ps -axj | grep test | grep -v grep; sleep 1; echo "-------------------"; done

        我们发现前面几秒确实都在运行,接着中间有两秒子进程处于僵尸状态,因为父进程比子进程多sleep两秒,正如我们所料;接着最后只剩父进程;子进程成功被父进程回收;

(2)waitpid函数

        下面为我们通过man手册查询结果;

参数pid:

pid作用
pid < -1等待进程组号为pid绝对值的任何子进程。
pid = -1等待任何子进程,此时的waitpid()函数就退化成了普通的wait()函数。
pid = 0等待进程组号与目前进程相同的任何子进程,也就是说任何和调用waitpid()函数的进程在同一个进程组的进程。
pid > 0等待进程号为pid的子进程。

 

注意:这里进程组号的暂不提及,我们用的也不多,平常用的较多的就是2和4;

参数status:

        这个参数为输出型参数,与我们wait函数相同;关于这个参数的使用,我们不能将里面的int值整体使用,得按比特位分开使用;

情况一:正常退出

        此时,我们使用第7到第15比特位,当作退出码;我们可以通过 (status >> 8) & 0xFF来获得这个退出码,还可以使用宏函数 WEXITSTATUS 来获取,通过宏函数 WIFEXITED 来获取进程是否正常退出,若正常退出返回真,否则返回假; 

情况二:信号终止退出(异常退出)

        这里我们用后面0到6的比特位来表示信号终止,我们可以使用 status & 0x7F来得到这个终止信号;至于这里的 core dump 标志位暂不讲解,这又是另一个话题了;

参数options:

        这个参数默认填0就好,表示阻塞等待,若填WNOHANG,则表示非阻塞等待;

返回值:

waitpid的返回值略比wait复杂一些,有三种情况;

1、正常返回,此时返回子进程pid;

2、若设置WNOHANG,且子进程还未退出,则返回0,子进程退出了,返回子进程pid;

3、调用失败,返回-1,错误码被设置;

代码实践:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>int main()
{int id = fork();if(id == -1){// fork调用失败exit(1);}else if(id == 0){// 子进程int cnt = 5;while(cnt--){printf("我是子进程,我的pid:%d,ppid:%d\n", getpid(), getppid());sleep(1);}exit(14); // 退出码}else  {// 父进程sleep(7);int status = 0;// 进程等待int ret = waitpid(id, &status, 0);  // 此时与我们的wait函数功能相同if(WIFEXITED(status)){printf("子进程正常退出,退出码为%d\n", WEXITSTATUS(status));}else{printf("子进程异常退出,收到信号%d\n", (status) & 0x7F);}sleep(2);}return 0;
}

        代码输出结果如下;

        若我们将代码加上一个除零错误;

        运行结果如下;

        我们再通过kill -l查看信号;8号信号正是我们的浮点数计算问题;

        我们再将代码改一下,将我们的waitpid改成非阻塞等待的情况,如下;

#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>int main()
{int id = fork();if(id == -1){// fork调用失败exit(1);}else if(id == 0){// 子进程int cnt = 5;// 除零错误展示// int a = 10/ 0;while(cnt--){printf("我是子进程,我的pid:%d,ppid:%d\n", getpid(), getppid());sleep(1);}exit(14); // 退出码}else  {// 父进程int status = 0;// 进程等待while(true){int ret = waitpid(id, &status, WNOHANG); if(ret == -1){printf("waitpid调用失败\n");exit(-1);}else if(ret == 0){printf("子进程还未退出,我再干点别的\n");sleep(1);}else{printf("等待成功\n");break;}}if(WIFEXITED(status)){printf("子进程正常退出,退出码为%d\n", WEXITSTATUS(status));}else{printf("子进程异常退出,收到信号%d\n", (status) & 0x7F);}sleep(2);}return 0;
}

        运行结果如下,此时我们的父进程就不用阻塞等待子进程结束了,父进程只需要通过轮询的方式来来回收子进程;

3、再次深刻理解进程等待

        上述内容为进程等待的实操部分,我们现在再次回到理论部分,我有如下问题;

问题一:我们是否可以通过一个全局变量来获取子进程的退出码呢?

        不可以,虽然父进程和子进程共用一段代码,但是都有各自的进程地址空间,当我们使用全局变量时,子进程往这个全局变量里写入时,会发生写时拷贝,因此无法获得退出码,这也是进程的独立性;

问题二:既然进程具有独立性,那么wait和waitpid是如何获取子进程的退出码的呢?

        我们的wait和waitpid属于系统调用,既然是系统调用,当然是属于操作系统的一部分,我们在回收子进程时,实际上是销毁进程PCB等内核数据的过程,而PCB(task_struct)中有一个退出码和退出信号的字段,以下为Linux源码截图;

        我们在task_struct里确实发现了这几个字段,如果有兴趣的,可以去官网下载一份源码,task_strcut结构体在 include/linux/sche.h 中;

        回到正题,既然我们task_struct中有这些字段,那么我们父进程回收子进程的时候是否可以获取这些字段的信息呢?答案当然是肯定的,我们的wait和waitpid为系统调用,当然有资格获取这些字段,我们的父进程也就可以通过这两个系统调用拿到了子进程的退出码了;

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

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

相关文章

pytorch复现4_Resnet

ResNet在《Deep Residual Learning for Image Recognition》论文中提出&#xff0c;是在CVPR 2016发表的一种影响深远的网络模型&#xff0c;由何凯明大神团队提出来&#xff0c;在ImageNet的分类比赛上将网络深度直接提高到了152层&#xff0c;前一年夺冠的VGG只有19层。Image…

uniapp 关于 video 组件的缩放比例问题

在 container 样式的 padding-bottom 设置比例值 9/16 比例值&#xff1a;56.25% 3/4 比例值&#xff1a;75% <view class"container"><video class"video-box" src"xxx.mp4" /> </view> .container {position: relative;wid…

Redis(01)| 数据结构

这里写自定义目录标题 Redis 速度快的原因除了它是内存数据库&#xff0c;使得所有的操作都在内存上进行之外&#xff0c;还有一个重要因素&#xff0c;它实现的数据结构&#xff0c;使得我们对数据进行增删查改操作时&#xff0c;Redis 能高效的处理。 因此&#xff0c;这次我…

作为20年老程序员,我如何使用GPT4来帮我写代码

如果你还在用google寻找解决代码bug的方案&#xff0c;那你真的out了&#xff0c;试试gpt4, save my life. 不是小编危言耸听&#xff0c;最近用gpt4来写代码极大地提高了代码生产力和运行效率&#xff0c;今天特地跟大家分享一下。 https://www.promptspower.comhttps://www.…

测开 (Junit 单元测试框架)

目录 了解 Junit 引入相关依赖 1、Junit注解 Test BeforeEach、BeforeAll AfterEach && AfterAll 2、断言 1、Assertions - assertEquals 方法 2、Assertions - assertNotEquals 方法 3、Assertions - assertTrue && assertFalse方法 4、Assertions…

Microsoft365个人版与家庭版有哪些功能区别?

Microsoft 365个人版与家庭版均能享受完整的Microsoft 365功能与权益&#xff0c;稍有不同的是&#xff0c;Microsoft 365家庭版可供6人使用&#xff0c;而个人版是仅供一人使用。 个人版可以同时登入5台设备&#xff0c;家庭版每人也可以登入5台设备&#xff0c;每个人都可以享…

【Linux】centos安装配置及远程连接工具的使用

前言 CentOS 是什么&#xff1f; CentOS社区企业操作系统&#xff08;Community Enterprise Operating System&#xff09; CentOS 是众多 Linux 发行版中的一种。全称&#xff1a; The Community ENTerprise Operating System 。 她是将 Red Hat Enterprise Linux &#xff…

sitespeedio.io 前端页面监控安装部署接入influxdb 到grafana

1.docker部署influxdb,部署1.8一下&#xff0c;不然语法有变化后面用不了grafana模板 docker run -d -p 8086:8086 --name influxdb -v $PWD/influxdb-data:/var/lib/influxdb influxdb:1.7.11-alpine docker exec -it influxdb_id bash #influx create user admin with pass…

Yakit工具篇:WebFuzzer模块之重放和爆破

简介 Yakit的Web Fuzzer模块支持用户自定义HTTP原文发送请求。为了让用户使用简单&#xff0c;符合直觉&#xff0c;只需要关心数据相关信息&#xff0c;Yakit后端(yaklang)做了很多工作。 首先我们先来学习重放请求的操作&#xff0c;在日常工作中可以使用 Web Fuzzer进行请…

无法查看 spring-boot-starter-parent的pom.xml

1. idea版本&#xff1a;2022.3 2. 使用Spring Initializr创建一个简单的spring-boot项目&#xff0c;发现无法查看 spring-boot-starter-parent的pom.xml ctrl鼠标左键 和 ctrl B 都无法进入 3. 解决&#xff1a;清除缓存重启&#xff08;&#x1f927;&#x1f630;&#…

计算机网络_04_传输层

文章目录 1.什么是传输层2.传输层提供了什么服务3.传输层协议TCP 1.什么是传输层 传输层是OSI七层体系架构中的第四层, TCP/IP四层体系架构中的第二层, 从通信和信息处理两方面来看&#xff0c;“传输层”既是面向通信部分的最高层&#xff0c;与下面的三层一起共同构建进行网…

木马免杀(篇三)静态免杀方法

紧接上一篇&#xff0c;是通过 cs 生成 shellcode 并直接用python 调用动态链接库执行 shellcode 。 生成后的exe文件未进行任何处理。 现在学习一些可以绕过静态免杀的方法。即将文件上传到目标不会被杀软查杀&#xff0c;但这只是静态方面。 动态免杀方面还涉及到很多东西&…

联手皇室企业 哪吒汽车发力阿联酋

布局阿联酋,哪吒汽车全球化战略加速落地。10月27日,哪吒汽车与阿联酋知名企业——EIH Automotive &Trading,在上海签署战略合作协议,并宣布2024年将为阿联酋带去多款车型。拥有皇室背景的EIH Automotive &Trading,将成为哪吒汽车在阿联酋的首家战略经销商,加速哪吒汽车…

取消Excel打开密码的两种方法

Excel设置了打开密码&#xff0c;想要取消打开密码是由两种方法的&#xff0c;今天分享这两种方法给大家。 想要取消密码是需要直到正确密码的&#xff0c;因为只有打开文件才能进行取消密码的操作 方法一&#xff1a; 是大家常见的取消方法&#xff0c;打开excel文件之后&a…

一天写一个(前端、后端、全栈)个人简历项目(附详源码)

一、项目简介 此项目是用前端技术HTMLCSSjquery写的一个简单的个人简历项目模板&#xff0c;图片可点击放大查看&#xff0c;还可以直接下载你的word或者PDF的简历模板。 如果有需要的同学可以直接拿去使用&#xff0c;需自行填写个人的详细信息&#xff0c;发布&#xff0c;…

​学习一下,什么是预包装食品?​

预包装食品&#xff0c;指预先定量包装或者制作在包装材料和容器中的食品&#xff1b;包括预先定量包装以及预先定量制作在包装材质和容器中并且在一定量限范围内具有统一的质量或体积标识的食品。简单说&#xff0c; 就是指在包装完成后即具有确定的量值&#xff0c;这一确定的…

【OpenVAS】一个快速、简洁的 OpenVAS 扫描解决方案

一. OpenVAS简介&#xff1a; 官网&#xff1a;http://www.openvas.org/ OpenVas是一个功能齐全的开源的漏洞扫描工具。它具有无身份验证和身份验证测试的功能&#xff0c;支持各种高级和低级互联网和工业协议&#xff0c;能够进行大规模扫描的性能调优&#xff0c;还提供强大…

实战 | SQL注入

一、资产搜集 我们都知道sql注入的传参有些是明文的&#xff0c;有些是经过编码或者加密的&#xff0c;所以我们搜索的时候不要仅限于inurl:.php?id1&#xff0c;可以额外的尝试搜搜1的base64编码值MQ&#xff0c;即可以搜索inurl:.php?idMQ&#xff0c;或者搜索1的md5加密值…

云原生-AWS EC2使用、安全性及国内厂商对比

目录 什么是EC2启动一个EC2实例连接一个实例控制台ssh Security groups规则默认安全组与自定义安全组 安全性操作系统安全密钥泄漏部署应用安全元数据造成SSRF漏洞出现时敏感信息泄漏网络设置错误 厂商对比参考 本文通过实操&#xff0c;介绍了EC2的基本使用&#xff0c;并在功…

光环云出席国际数据经济产业合作大会,成为国际数据经济产业园首批生态合作企业

光环云作为临港新片区国际数据港全球云算服务生态合作伙伴受邀出席会议&#xff0c;与跨境数科等单位共同参与共建国际数据港启动仪式&#xff0c;光环云执行董事兼CEO吴曼以《AGI-x时代跨境数据流动》为主题作主旨演讲。 10月27日&#xff0c;国际数据经济产业合作大会在临港新…