当前位置: 首页 > news >正文

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,已经写时拷贝过了,因此可以获取。

相关文章:

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

目录 前言 一、进程等待 二、如何进行进程等待 1.wait 2.waitpid 2.1第二个参数 2.2第三个参数 3. 等待多个进程 三、为什么不用全局变量获取子进程的退出信息 前言 前面我们花了大量的时间去学习进程的退出&#xff0c;退出并不难&#xff0c;但更深入的学习能为本…...

多线程导入excel

设置线程池参数&#xff0c;创建线程池 corePoolSize要保留在池中的线程数&#xff0c;即使它们是空闲的&#xff0c;除非{code - allowCoreThreadTimeOut}被设置maximumPoolSize允许在池中的最大线程数keepAliveTime当线程数大于核心时&#xff0c;这是多余的空闲线程将在终止…...

设计模式(十五)状态模式

请直接看原文:设计模式系列 ------------------------------------------------------------------------------------------------------------------------------- 前言 建议在阅读本文前先阅读设计模式&#xff08;十一&#xff09;策略模式这篇文章&#xff0c;虽说状态…...

Java基于SpringBoot的在线文档管理系统的设计与实现论文

摘 要 随着科学技术的飞速发展&#xff0c;社会的方方面面、各行各业都在努力与现代的先进技术接轨&#xff0c;通过科技手段来提高自身的优势&#xff0c;在线文档管理当然也不能排除在外。在线文档管理系统是以实际运用为开发背景&#xff0c;运用软件工程原理和开发方法&am…...

突破编程_C++_STL教程( list 的高级特性)

1 std::list 的排序 1.1 基础类型以及 std::string 类型的排序 std::list的排序可以通过调用其成员函数sort()来实现。sort()函数使用默认的比较操作符&#xff08;<&#xff09;对std::list中的元素进行排序。这意味着&#xff0c;如果元素类型定义了<操作符&#xff…...

Scratch 第十六课-弹珠台游戏

第十六课-弹珠台游戏 大家好&#xff0c;今天我们一起做一款弹珠台scratch游戏&#xff0c;我们也可以叫它弹球游戏&#xff01;这款游戏在刚出来的时候非常火爆。小朋友们要认真学习下&#xff01; 这节课的学习目标 物体碰撞如何处理转向问题。复习键盘对角色的控制方式。…...

对简单工厂模式、工厂方法模式的思考

目录 1 背景1.1 题目描述1.2 输入描述1.3 输出描述1.4 输入示例1.5 输出示例 2 简单工厂模式3 工厂方法模式4 思考4.1 改进工厂方法模式 1 背景 题目源自&#xff1a;【设计模式专题之工厂方法模式】2.积木工厂 1.1 题目描述 小明家有两个工厂&#xff0c;一个用于生产圆形积木…...

【详识JAVA语言】面向对象程序三大特性之二:继承

继承 为什么需要继承 Java中使用类对现实世界中实体来进行描述&#xff0c;类经过实例化之后的产物对象&#xff0c;则可以用来表示现实中的实体&#xff0c;但是 现实世界错综复杂&#xff0c;事物之间可能会存在一些关联&#xff0c;那在设计程序是就需要考虑。 比如&…...

【剑指offer--C/C++】JZ3 数组中重复的数字

一、题目 二、本人思路及代码 这道题目它要求的时间空间利用率都是n&#xff0c;那么可以考虑创建一个长度为n的数组repeat初始化为0&#xff0c;下标代码出现的数字&#xff0c;下标对应的数组内容代表该下标数字出现的次数。然后遍历提供的数组&#xff0c;每出现一个数字&a…...

基于SpringBoot的在线拍卖系统设计与实现(源码)

项目源码&#xff1a;https://gitee.com/oklongmm/biye2 引言 随着互联网技术的发展&#xff0c;电子商务得以快速发展&#xff0c;其中之一的在线拍卖系统也逐渐得到了广泛的应用。但是&#xff0c;现有的在线拍卖系统在操作复杂性、安全性和稳定性方面存在一定的问题。为了…...

卢森堡比利时土耳其媒体宣发稿助力跨境出海推广新闻营销

【本篇由言同数字科技有限公司原创】随着全球化进程的加速&#xff0c;越来越多的品牌开始考虑在海外市场扩展业务。对于品牌来说&#xff0c;跨境海外推广是必要的&#xff0c;因为它可以帮助品牌打开更大的市场、吸引更多的消费者、提高品牌知名度和形象&#xff0c;并在全球…...

冒泡排序(C语言详解)

原理&#xff1a;从左到右一次比较&#xff0c;如果左侧数字比右侧数字大&#xff08;小&#xff09;&#xff0c;则两数交换&#xff0c;否则比较下一 组数字&#xff0c;每一次大循环比较可以将乱序的最右侧数字改为最大&#xff08;最小&#xff09;&#xff0c…...

STC-ISP原厂代码研究之 V3.7d汇编版本

最近在研究STC的ISP程序,用来做一个上位机烧录软件,逆向了上位机软件,有些地方始终没看明白,因此尝试读取它的ISP代码,但是没有读取成功。应该是目前的芯片架构已经将引导代码放入在了单独的存储块中,而这存储块有硬件级的使能线,在面包板社区-宏晶STC单片机的ISP的BIN文…...

【word】引用文献如何标注右上角

一、在Word文档中引用文献并标注在右上角的具体步骤如下 1、将光标移动到需要添加文献标注的位置&#xff1a; 2、在文档上方的工具栏中选择“引用”选项&#xff1a; 3、点击“插入脚注”或“插入尾注”&#xff1a; ①如果选择的是脚注&#xff0c;则脚注区域会出现在本页的…...

MySQL 5.5、5.6、5.7的主从复制改进

主从复制面临的问题 MySQL一直以来的主从复制都是被诟病,原因是: 1、主从复制效率低 早期mysql的复制是通过将binlog语句异步推送到从库。从库启动一个IO线程将接收到的数据记录到relaylog中;另外启动一个SQL线程负责顺序执行relaylog中的语句实现对数据的拷贝。 这里的…...

性能分析排查思路之日志(1)

本文是性能问题分析排查思路的展开内容之一&#xff0c;主要分为日志1期&#xff0c;机器4期、环境2期共7篇系列文章&#xff0c;本期是第一篇&#xff0c;讲日志的分析方法和经验。 系列文章传送门&#xff1a; 一图梳理性能问题分析排查思路-总体概述&#xff08;0&#xff…...

Vue中如何实现条件渲染?

在Vue中实现条件渲染非常简单且灵活&#xff0c;主要通过Vue的指令来实现。在Vue中&#xff0c;我们可以使用v-if和v-else指令来根据条件来渲染不同的内容。下面就让我们通过一个简单的示例来演示如何在Vue中实现条件渲染&#xff1a; <!DOCTYPE html> <html lang&qu…...

Postman上传文件的操作方法

前言 调用某个接口&#xff0c;测试上传文件功能。一时间不知如何上传文件&#xff0c;本文做个操作记录&#xff0c;期望与你有益。 步骤一、设置Headers key:Content-Type value:multipart/form-data 步骤二、设置Body 选择form-data key:file下拉框选择file类型value&…...

linux系统Jenkins工具介绍

Jenkins概念介绍 Jenkins概念Jenkins目的特性产品发布流程 Jenkins概念 Jenkins是一个功能强大的应用程序&#xff0c;允许持续集成和持续交付项目&#xff0c;无论用的是什么平台。这是一个免费的源代码&#xff0c;可以处理任何类型的构建或持续集成。集成Jenkins可以用于一些…...

【python】遵守 robots.txt 规则的数据爬虫程序

程序1 编写一个遵守 robots.txt 规则的数据爬虫程序涉及到多个步骤&#xff0c;包括请求网页、解析 robots.txt 文件、扫描网页内容、存储数据以及处理异常。由于编程语言众多&#xff0c;且每种语言编写爬虫程序的方式可能有所不同&#xff0c;以下将使用 Python 语言举例&am…...

Java 语言特性(面试系列2)

一、SQL 基础 1. 复杂查询 &#xff08;1&#xff09;连接查询&#xff08;JOIN&#xff09; 内连接&#xff08;INNER JOIN&#xff09;&#xff1a;返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...

MySQL 隔离级别:脏读、幻读及不可重复读的原理与示例

一、MySQL 隔离级别 MySQL 提供了四种隔离级别,用于控制事务之间的并发访问以及数据的可见性,不同隔离级别对脏读、幻读、不可重复读这几种并发数据问题有着不同的处理方式,具体如下: 隔离级别脏读不可重复读幻读性能特点及锁机制读未提交(READ UNCOMMITTED)允许出现允许…...

k8s业务程序联调工具-KtConnect

概述 原理 工具作用是建立了一个从本地到集群的单向VPN&#xff0c;根据VPN原理&#xff0c;打通两个内网必然需要借助一个公共中继节点&#xff0c;ktconnect工具巧妙的利用k8s原生的portforward能力&#xff0c;简化了建立连接的过程&#xff0c;apiserver间接起到了中继节…...

是否存在路径(FIFOBB算法)

题目描述 一个具有 n 个顶点e条边的无向图&#xff0c;该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序&#xff0c;确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数&#xff0c;分别表示n 和 e 的值&#xff08;1…...

Linux --进程控制

本文从以下五个方面来初步认识进程控制&#xff1a; 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程&#xff0c;创建出来的进程就是子进程&#xff0c;原来的进程为父进程。…...

OPENCV形态学基础之二腐蚀

一.腐蚀的原理 (图1) 数学表达式&#xff1a;dst(x,y) erode(src(x,y)) min(x,y)src(xx,yy) 腐蚀也是图像形态学的基本功能之一&#xff0c;腐蚀跟膨胀属于反向操作&#xff0c;膨胀是把图像图像变大&#xff0c;而腐蚀就是把图像变小。腐蚀后的图像变小变暗淡。 腐蚀…...

Java线上CPU飙高问题排查全指南

一、引言 在Java应用的线上运行环境中&#xff0c;CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时&#xff0c;通常会导致应用响应缓慢&#xff0c;甚至服务不可用&#xff0c;严重影响用户体验和业务运行。因此&#xff0c;掌握一套科学有效的CPU飙高问题排查方法&…...

Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析

Java求职者面试指南&#xff1a;Spring、Spring Boot、MyBatis框架与计算机基础问题解析 一、第一轮提问&#xff08;基础概念问题&#xff09; 1. 请解释Spring框架的核心容器是什么&#xff1f;它在Spring中起到什么作用&#xff1f; Spring框架的核心容器是IoC容器&#…...

IP如何挑?2025年海外专线IP如何购买?

你花了时间和预算买了IP&#xff0c;结果IP质量不佳&#xff0c;项目效率低下不说&#xff0c;还可能带来莫名的网络问题&#xff0c;是不是太闹心了&#xff1f;尤其是在面对海外专线IP时&#xff0c;到底怎么才能买到适合自己的呢&#xff1f;所以&#xff0c;挑IP绝对是个技…...

解读《网络安全法》最新修订,把握网络安全新趋势

《网络安全法》自2017年施行以来&#xff0c;在维护网络空间安全方面发挥了重要作用。但随着网络环境的日益复杂&#xff0c;网络攻击、数据泄露等事件频发&#xff0c;现行法律已难以完全适应新的风险挑战。 2025年3月28日&#xff0c;国家网信办会同相关部门起草了《网络安全…...