Linux——进程控制(二)进程等待

目录

前言

一、进程等待

二、如何进行进程等待

1.wait

2.waitpid

2.1第二个参数 

2.2第三个参数 

3. 等待多个进程

三、为什么不用全局变量获取子进程的退出信息


前言

前面我们花了大量的时间去学习进程的退出,退出并不难,但更深入的学习能为本章进程等待打好基础,因此没看过的小伙伴可以先学习进程退出。

一、进程等待

之前讲过,子进程退出,父进程一直在运行,不对子进程进行回收,就可能造成‘僵尸进程’的问题,进而造成内存泄漏

另外,进程一旦变成僵尸状态,那就刀枪不入,“杀人不眨眼”的kill -9 也无能为力,因为谁也没有办法 杀死一个已经死去的进程。

父进程派给子进程的任务完成的如何,我们需要知道。如子进程运行完成,结果对还是不对, 或者是否正常退出。

父进程通过进程等待的方式,可以获取子进程退出的信息。(虽然不是一定要获取,但是得有这个功能)

二、如何进行进程等待

1.wait

我们看看2号手册中的wait函数,他可以等待任意一个子进程的退出,参数是int类型的指针,等待成功返回子进程的pid,失败返回-1。

我们使用如下代码进行进程等待,这里wait的参数先给NULL,代表不关心子进程退出的状态(后续会再提到)。子进程运行5秒后变成僵尸状态,父进程先休眠10秒再去调用wait函数。

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>void Work()
{int cnt = 5;while(cnt){printf("我是子进程, pid: %d, ppid: %d, cnt: %d\n",getpid(),getppid(),cnt--);sleep(1);}
}int main()
{pid_t id = fork();if(id == 0){//childWork();exit(0);}else{sleep(10);//fatherpid_t rid = wait(NULL);if(rid == id){printf("等待成功,pid: %d\n",getpid());}}return 0;
}

我们写了一个脚本来监控进程的运行情况,代码如下(注意myprocess是我设置的进程名)

 while :; do ps ajx | head -1 && ps ajx | grep myprocess | grep -v "grep"; sleep 1; echo "###################"; done

结果发现0-5秒中,父子进程正常运行,5-10秒中,子进程变成了僵尸状态,父进程此时在sleep,并没有回收子进程,10秒后,父进程sleep结束,wait函数等到了子进程,于是将子进程回收了,同时父进程也运行完毕。 

由此我们可以得知:

进程等待能回收子进程僵尸状态。

还有一个结论,在父进程进行等待的时候,如果子进程还没有处理完,那么父进程必须在wait上进行阻塞等待,直到子进程僵尸,wait自动回收,再继续执行后续代码。这可以通过打印的方式查看。就类似于scanf需要等待用户输入一样,用户不输入就一直在这里阻塞着,直到输入后才继续往后执行。

一般而言,父子进程谁先运行我们不知道,但能知道一般都是父进程最后退出,多进程由父进程发起,也由父进程统一回收

2.waitpid

wait是等待任意一个子进程,而waitpid可以等待指定的那一个,第一个参数传等待子进程的pid代表等待这个进程,传-1代表等待任意进程。 第二个参数和wait的参数一样,第三个参数也先不管,设置为0代表默认阻塞等待。

将上面的代码从wait修改为waitpid,因为我们只fork了一次,只创建了一个子进程,因此如下修改即可。

2.1第二个参数 

重点我们得讲解一下第二个参数 status ,他是输出型参数,我们可以随便定义一个int变量,将变量的值传递给第二个参数,waitpid会将子进程退出码和信号写到这个变量里

这里我们将子进程的退出码设置为10,看看打印出来的status值为多少。

发现status为2560,这似乎不像退出码。他是通过下面这个图片的方式得来的,int整形32位,只用低16位,其中高8位代表退出码,低8位代表终止信号

正常终止看高八位即可,因为未收到信号,因此低8位为0。

被信号所杀,看低八位,其中第7位不看,他代表core dump标志(暂时不考虑),只看0-6位。 

那么2560的二进制为 0000 1010 0000 0000 如果右移8位,也就是只看高8位,即0000 1010,即为10,我们退出码也就是10。

公式为  :  *status = (exit_code<<8)| exit_signal

status不能直接使用,如下经过右移操作和与操作,就可以得到准确的退出码和信号了。

执行一下,没有问题 

小总结一下

  • 当一个进程异常了(收到信号) ,那么退出码就无意义了
  • 通过信号码是否非零,来判断是否收到信号
  • 手动杀死子进程,也能得到相应信号

虽然我们会通过位运算来得到退出码与信号,但这样也比较麻烦,linux系统提供了如下两个接口帮我们处理status。

WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)

WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)

如下,查看是否正常退出,退出码为多少。 

结果也符合预期 

2.2第三个参数 

0:即阻塞等待

WNOHANG::若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程的ID。(非阻塞式等待)

讲个小故事:

        张三约翠花去电竞酒店打麻将, 他已经到翠花楼下了,现在在等待翠花先来一起出发。

        此时张三有两个方法,一个是打电话,询问翠花在干嘛,什么时候下楼,打完翠花说等一下,她化个妆,于是张三就在楼下开一把金铲铲之战,过了半个小时又打,翠花说在穿鞋了等一小下,于是张三挂断电话去刷抖音,过一会再打电话,翠花说已经下楼了,刚刚准备出门又上了个厕所,张三没有什么脾气,谁叫我想约人家呢,于是挂断电话,又去看看淘宝,要买点什么,最后再打电话,翠花此时终于到达了,于是两个人开开心心的去打麻将了。

        另一个方法也是打电话,询问翠花在干嘛,什么时候下楼,翠花也说等一下,还在化妆,但是张三今天电话不挂,就一直等翠花,时刻知道翠花在干嘛,直到翠花下楼一起去打麻将。

在这个故事中

张三:父进程

翠花:子进程

打电话:调用系统接口

第一个方法:等待的条件不满足,wait/waitpid不阻塞,而是立即返回!可以做自己占据时间并不多的事情。这是非阻塞式调用,即非阻塞+轮询方案进行进程等待,该方案往往要进行重复调用。返回值>0等待成功,子进程已退出;返回值==0;等待成功,子进程未退出,返回值<0等待失败

第二个方法:翠花不结束,电话不挂机,即阻塞式调用。子进程不退出,wait/waitpid不返回。

代码如下,waitpid第三个参数为 WNOHANG 借此观看非阻塞轮询等待。 

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>void Work(int number)
{printf("我是子进程, pid: %d, ppid: %d, number: %d\n",getpid(),getppid(),number);
}int main()
{pid_t id = fork();if(id == 0){//childint number = 5;while(number){Work(number);number--;sleep(1);}exit(10);}//father                                                                                          int status = 0;while(1){pid_t rid = waitpid(id,&status,WNOHANG);if(rid>0){//等待成功,子进程退出了printf("等待子进程成功,子进程退出码: %d,退出信号: %d\n",WEXITSTATUS(status),status&0x7F);break;}else if(rid == 0){//等待成功,但子进程没有退出printf("等待成功,子进程还没推出,父进程做其他事情去了\n");sleep(2);}else{printf("等待失败\n");break;}}return 0;
}

运行结果如下,父进程间歇性询问子进程是否完成,没完成就做自己的事情,待会再来询问。

3. 等待多个进程

我们使用for循坏来fork多个进程,同时给每个进程编号,创建顺序从0-9。waitpid第一个参数为-1,代表等待任意的子进程。

虽然我们也可以用数组的方式,将子进程的pid放到数组里,但是这样就只能一个一个进程的等待,比如最先会等待退出码为0进程,如果该进程不结束,父进程会一直等待,那么后续的进程永远不会被回收,就会造成内存泄漏的问题。

 1: myprocess.c ? ?                                                                        ?? buffers 
#include<sys/types.h>
#include<sys/wait.h>void Work(int number)
{int cnt = 2;while(cnt){printf("我是子进程, pid: %d, ppid: %d, cnt: %d, number: %d\n",getpid(),getppid(),cnt--,number);sleep(1);}
}const int n = 10;int main()
{int i = 0;for(;i<n;i++){pid_t id = fork();if(id == 0){//childWork(i);exit(i);}}//fork的子进程已近全部退出了,下面是父进程执行的代码for(i=0;i<n;i++){int status;pid_t rid = waitpid(-1,&status,0);  //-1:任意一个子进程退出 if(rid>0)                                                                                      {printf("等待子进程 %d 成功, 退出码: %d\n",rid, WEXITSTATUS(status));}}return 0;
}

看看运行的情况,发现调度运行与终止都是没有规律的,谁先谁后我们不确定,我们只知道肯定是父进程先创建并最后退出。

三、为什么不用全局变量获取子进程的退出信息

刚刚我们提到, 可以用数组获取子进程的pid,然后传值进行等待,虽然效果不一定很好,但这也算是一个解决办法,为什么不用全局变量获取子进程的退出信息,而是采用写入的方式进行传参获取呢?

这是因为进程之间具有独立性,父进程无法直接获取子进程的退出信息,比如status我们设置为0,父子进程看到的status值就都为0,此时我们获取到了子进程的退出码,将子进程的退出码写入status变量,此时会发生写时拷贝,子进程看到的是我自己写的新值,而父进程看到的还是0。父子进程代码共享,但数据不一定相同

而父进程通过fork,返回的id是子进程的pid,子进程返回的id为0,已经写时拷贝过了,因此可以获取。

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

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

相关文章

048 异常

什么是异常 异常体系结构 异常的继承关系 Error Exception 异常处理机制 try&#xff1a;用{}将可能产生异常的代码包裹catch&#xff1a;与try搭配使用&#xff0c;捕获try包裹代码中抛出的异常并进行后续动作finally&#xff1a;跟在try后&#xff0c;在try和catch之后执行…

web3时事粥报

比特币正成为更具有吸引力的通胀对冲工具 在通胀的宏观经济浪潮中&#xff0c;比特币正逐渐崭露头角&#xff0c;成为那些渴望多元化投资组合的投资者眼中的璀璨明星。Kooner 预测&#xff0c;2024年&#xff0c;各种宏观经济挑战可能进一步提升比特币、黄金和白银等资产的避险…

Google Dremel和parquet的复杂嵌套数据结构表征方法解析

转载请注明出处。作者&#xff1a;archimekai 核心参考文献&#xff1a; Dremel: Interactive Analysis of Web-Scale Datasets 文章目录 引言复杂嵌套数据结构的无损表征问题Dremel论文中提出的表征方法parquet备注 引言 Dremel是Google的交互式分析系统。Google大量采用prot…

搭建服务器及跨域处理

使用内置的模块搭建服务器 自己电脑: 域名:localhost ip:127.0.0.1 http模块搭建服务器 const http = require(http)// 创建一个http对应的服务器,每次改完服务器的代码后都需要重新启动下服务器 /*方式一: const server = http.createServer((request,response)=>{…

铁路关基保护新规发布!铁路软件供应链安全洞察与治理思路

近日&#xff0c;国家铁路局发布《铁路关键信息基础设施安全保护管理办法》&#xff0c;《办法》第十四条提到&#xff1a;“运营者应当加强铁路关键信息基础设施供应链安全保护&#xff0c;优先采购安全可信的网络产品和服务。运营者采购网络产品和服务&#xff0c;应当预判该…

Intel FPGA IP之LVDS SerDes IP学习

FPGA 视频数据输入输出直通工程&#xff1a; 屏&#xff1a;13.2吋8bit色深&#xff0c;屏幕分辨率为1440*192060&#xff0c;具有两个Port&#xff0c;每个Port有4个差分数据对与1个差分时钟对&#xff0c;差分对均支持LVDS协议芯片&#xff1a;Cyclone V系列FPGA目的&#x…

Noise Conditional Score Networks(NCSN)学习

参考&#xff1a; [1] https://zhuanlan.zhihu.com/p/597490389 [2] https://www.zhangzhenhu.com/aigc/Score-Based_Generative_Models.html TOC 1 基于分数的生成模型1.1 简介和动机1.2 Score Matching及其改进1.2.1 Score Matching1.2.2 Sliced score matching&#xff08;不…

XSS_lab(level1-level5)

level1 直接输入页面没有发现输入框&#xff0c;观察url发现有传参 尝试修改传参为&#xff1a;<script>alert(1)</script> 过啦&#xff01; level2 页面中有输入框&#xff0c;尝试构建语句&#xff1a;<script>alert(1)</script>,传输后查看源代…

国际心理学导师-叶子文JeffreyYip的《意识地图》

“物质就是能量。” ---爱因斯坦 “時常保持觉知&#xff0c;有意识地发现情绪起伏 你随时都能翻转人生 做自己人生的导演 当你频率高时&#xff0c;万事万物为你而来” ---大卫霍金斯 叶子文-《意识地图》&#xff1a;高阶心理学课程 宇宙间万物的本质是能量。一切都靠能量…

Java基础---lambda表达式

一、为什么要引入lambda表达式 lambda 表达式是一个可传递的代码块 &#xff0c; 可以在以后执行一次或多次 。 在介绍lambda表达式之前&#xff0c;我们看一下&#xff0c;以前&#xff0c;我们对于一个问题的通常写法。 假设你已经了解了如何按指定时间间隔完成工作&#xf…

js字符串转json的3种方法

1.eval方式解析 function strToJson(str){var json eval("(" str ")");return json;}console.log(strToJson("{int:1, string:demo}")); 运行截图&#xff1a; 注&#xff1a; 记得别忘了str两旁的小括号。 永远不要使用 eval !!! eval() 是一…

611. 有效三角形的个数 - 力扣

1. 题目 给定一个包含非负整数的数组 nums &#xff0c;返回其中可以组成三角形三条边的三元组个数。 2. 示例 3. 分析 利用已升序了的数组通过 a b > c 这条公式找出符合要求的三元组&#xff0c;利用这个公式的前提是三条边为从小到大&#xff0c;再利用单调性快速统计…

STM32 (1)

1.基本信息 stm32是由ST公司生产的一种32位微控制器&#xff08;单片机&#xff09;。 1.1 各种型号 stm32是32位单片机的总称&#xff0c;有多种不同的系列。 32即用32个比特位表示一个地址&#xff0c;寻址范围&#xff1a;0x00000000 --0xffffffff (4GB) 1.2 存储密度 …

UE5中实现后处理深度描边

后处理深度描边可以通过取得边缘深度变化大的区域进行描边&#xff0c;一方面可以用来做角色的等距内描边&#xff0c;避免了菲尼尔边缘光不整齐的问题&#xff0c;另一方面可以结合场景扫描等特效使用&#xff0c;达到更丰富的效果&#xff1a; 后来解决了开启TAA十字线和锯齿…

XXL-Job的基本使用

一、市面上常见的任务调度产品 针对分布式任务调度的需求&#xff0c;市场上出现了很多的产品: 其中XXL-job 是我们经常使用的任务调度平台,XXL这三个英文字母.是以作者名许雪里命名的。 可以前往 Gitee 地址进行下载使用 https://gitee.com/xuxueli0323/xxl-job.git二、XXL-J…

第二十一周周报

文献阅读&#xff1a;Recent Advances of Monocular 2D and 3D Human Pose Estimation: A Deep Learning Perspective 摘要&#xff1a;在本文中&#xff0c;作者提供了一个全面的 2d到3d视角来解决单目人体姿态估计的问题。首先&#xff0c;全面总结了人体的二维和三维表征。…

Windows的Linux化持续推进中

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

Java基础 - 6 - 面向对象(二)

Java基础 - 6 - 面向对象&#xff08;一&#xff09;-CSDN博客 二. 面向对象高级 2.1 static static叫做静态&#xff0c;可以修饰成员变量、成员方法 2.1.1 static修饰成员变量 成员变量按照有无static修饰&#xff0c;分为两种&#xff1a;类变量、实例变量&#xff08;对象…

故障诊断 | 一文解决,XGBoost极限梯度提升树的故障诊断(Matlab)

效果一览 文章概述 故障诊断 | 一文解决,XGBoost极限梯度提升树的故障诊断(Matlab) 模型描述 XGBoost通过集成多个决策树来建立一个强大的预测模型。它采用了一种特殊的梯度提升技术,称为极限梯度提升(Extreme Gradient Boosting),以提高模型的性能和鲁棒性。 极限梯度…

【大数据Hive】hive 多字段分隔符使用详解

目录 一、前言 二、hive默认分隔符规则以及限制 2.1 正常示例&#xff1a;单字节分隔符数据加载示例 2.2 特殊格式的文本数据&#xff0c;分隔符为特殊字符 2.2.1 文本数据的字段中包含了分隔符 三、突破默认限制规则约束 3.1 数据加载不匹配情况 1 3.2 数据加载不匹配…