【Linux详解】进程等待 | 非阻塞轮询
引入:
为什么?是什么?怎么办
是什么?
进程等待是指父进程暂停自己的执行,直到某个特定的子进程结束或发生某些特定的事件。
为什么?
- 僵尸进程刀枪不入,不可被杀死,存在内存泄露
- 获取进程的执行情况,知道我布置给子进程的任务,它完成的怎么样了–可选
怎么办
wait 函数
#include <sys/types.h>
#include <sys/wait.h>pid_t wait(int* status);
测试:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>int main (void)
{pid_t id = fork();if (id == 0) {// childwhile (1) {printf("我是子进程,我正在运行... Pid: %d\n", getpid());sleep(1);}}else {printf("我是父进程: pid: %d,我将耐心地等待子进程!\n", getpid());sleep(20); // 为了便于观察,我们让父进程休眠20s// 苏醒后,父进程执行 wait,耐心地等待子进程pid_t ret = wait(NULL); // 暂且将status参数设置为NULLif (ret < 0) {printf("等待失败!\n");}else {printf("等待成功!\n"); // 此时 Z → X}sleep(20); // 子进程退出后,再让父进程存在一段时间}
}
为什么是交替型,不是一个执行完?//了解循环的细节
在 fork
创建子进程后,父进程会首先执行并打印这条信息,其中 <父进程的pid>
是父进程的进程ID。截不下就没截了
- 父进程打印一次消息后休眠20秒:
- 父进程仅在创建子进程后打印一次消息,然后休眠20秒等待观察子进程的活动,不再进行其他操作。
通过这种方式,父进程和子进程能够并发地执行各自的任务,并且我们通过延迟父进程的退出能观察到子进程的连续活动。
整个程序执行流程如下:
-
父进程打印:
yamlCopy code 我是父进程: pid: 1234,我将耐心地等待子进程!
-
子进程每秒打印一次:
yamlCopy code 我是子进程,我正在运行... Pid: 5678
(此循环持续到子进程被终止)
-
通过终端输入
kill 5678
终止子进程。 -
父进程20秒后苏醒并等待子进程结束后,打印:
textCopy code 等待成功!
之后,父进程再休眠20秒并继续存在一段时间后退出
waitpid
刚才讲的 wait 并不是主角,因为其功能比较简单,在进程等待时用的更多的是 waitpid
waitpid 可以把 wait 完全包含,wait 是 waitpid 的一个子功能。
参数:
-
pid
:要等待的子进程的进程ID。根据传入的值可以指定等待任何子进程、特定进程ID的子进程、任何同一进程组的子进程或者任何同一会话的子进程。
- 如果
pid
大于零,waitpid
将等待指定进程ID的子进程结束。 - 如果
pid
等于 -1,waitpid
将等待任意子进程结束,等同于wait
函数。
- 如果
-
status
:一个指向整型的指针,是一个输出型参数,它将用于存储子进程的终止状态。 -
options
:用于指定等待行为的附加选项。
- 传入0表示以默认行为等待子进程。(阻塞等待中再讲)
返回值:
- 如果
waitpid
成功,返回值是已终止子进程的进程ID。 - 如果出现错误,返回-1,并且会设置
errno
变量来指示具体错误原因。
Z状态,其本质上就是将自己的 task_struct 维护起来(代码可以释放,但是 task_struct 必须维护)。所谓的 wait/waitpid 的退出信息,实际上就是从子进程的 task_struct 中拿出来的,即 从子进程的 task_struct 中拿出子进程退出的退出码,拷贝到父进程中
status
该参数是一个 输出型参数 (即通过调用该函数,从函数内部拿出来特定的数据)。整数的低 16 位,其中又可以分为 最低八位 和 次低八位(具体细节看图):
1.次低八位:拿子进程退出码
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>int main (void)
{pid_t id = fork();if (id == 0) {int cnt = 5; // 循环5次// childwhile (1) {// 五秒之内运行状态printf("我是子进程,我正在运行... Pid: %d\n", getpid());sleep(1);// 五秒之后子进程终止cnt--;if (cnt == 0) {break; }}exit(233); // 方便辨识,退出码我们设置为233,这是我们的预期结果}else {printf("我是父进程: pid: %d,我将耐心地等待子进程!\n", getpid());// ***** 使用waitpid进行进程等待int status = 0; // 接收 waitpid 的 status 参数pid_t ret = waitpid(id, &status, 0);if (ret > 0) { // 等待成功printf ("等待成功,ret: %d, 我所等待的子进程退出码: %d\n", ret,(status>>8)&0xFF);}}
}
status 并不是整体使用的,而是区域性使用的,我们要取其次低八位。我们可以用 位操作 来完成,将 status右移八位再按位与上 0XFF,即 (status>>8)&0xFF ,就可以提取到 status 的次低八位了。
waitpid 经过系统调用,来读取子进程的pcb(eg. task_st…),这是为什么呢
操作系统不相信任何人,父进程用户无法直接读取子进程的 pcb ,要通过系统调用的接口
2.初识 core dump(核心转储)
它是操作系统在进程收到某些信号而终止运行时,将此时进程地址空间的内容以及有关进程状态的其他信息写出的一个磁盘文件。目前只需要知道,该信息是用于调试的。
3.最低七位:提取子进程的退出信号
刚才我们讲的 wait/waitpid 和次低八位的时侯,都是关于进程(exit) 的 正常退出。
如果进程 异常退出 呢?我们来模拟一下进程的异常退出。
💬 模拟异常退出的情况,让子进程一直跑,父进程一直等。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>int main (void)
{pid_t id = fork();if (id == 0) {// 子进程一直不退出,父进程会一直等待。// childwhile (1) {printf("我是子进程,我正在运行... Pid: %d\n", getpid());sleep(1);}exit(13);}else {printf("我是父进程: pid: %d,我将耐心地等待子进程!\n", getpid());int status = 0;pid_t ret = waitpid(id, &status, 0);if (ret > 0) { // 等待成功printf("等待成功,ret: %d, 我所等待的子进程退出码: %d\n, 退出信号是: %d", ret, (status>>8)&0xFF, status&0x7F);}}
}
现在我们直接 while(1) 死循环让子进程往死里跑,此时父进程由于调用了 waitpid,就会一直等待子进程,父进程就会持续阻塞。
父进程看到子进程kill了,终于可以不用等了,可以给子进程收尸了
可以发现,使用-9号信号kill掉进程时,进程的退出信号就是9,然而当进程由于信号异常终止时,此时进程退出码是无意义的!
所以进程的等待可以理解为是 父进程在等给子进程退出记录
非阻塞轮询
options ->阻塞方式
waitpid(pid,&status,WNOHANG);
WNOHANG
就是wait no hang,hang也就是悬挂,也就是非阻塞,等待子进程死亡,若父进程执行到waitpid
时,子进程还没退出,则函数返回0后接着运行下面的代码,若执行到waitpid
后子进程已经退出则返回退出子进程的pid
假如要期末考试了,我想找小张去自习室帮我复习,小张自己也在复习,我打电话问他什么时候复习完了,可以出门,情况解释如下
WNOHANG
夯住,非阻塞+循环
我给小张间隔打电话寻求帮忙,自己干等在楼下
阻塞式调用
打电话一直不挂
非阻塞轮询+自己的事情(最高效)
间隔打电话,自己也在复习
返回值
pid_t
ret_pid=0–所等待的条件还没有就绪,>0成功返回退出码,<0失败
代码验证
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{pid_t id = fork();if(id<0){perror("fork");exit(1);}if(id==0)//子进程代码{int count = 5;while(count){printf("[%d]我是子进程,我的pid是: %d\n",count,getpid());sleep(1);count--;}exit(55);//子进程执行完代码后退出}//父进程代码while(1)//循环访问子进程退出情况{int wait = waitpid(id,NULL,WNOHANG);if(wait>0)//子进程退出成功{printf("子进程退出成功,子进程pid: %d\n",wait);break;}else if(wait==0)//子进程还没退出,父进程干自己的事情{//此处简单模拟父进程干的事情printf("我是父进程,我现在要干一些别的事情\n");}else //等待子进程退出失败{perror("waitpid");exit(1);}sleep(1);}return 0;
}
⚠️注:这里父进程可以执行任一任务,我使用printf打印,只是为了明显的看到父进程是没有阻塞等待的!
进程退出的宏
为了增加可读性,定义了接口宏,来查找退出码
WEXITSTATUS
和WIFEXITED
,在这之前,我们再思考一个问题:
❓ **思考:**一个进程退出时,可以拿到退出码和推出信号,我们先看谁?
一旦程序发现异常,我们只关心退出信号,退出码没有任何意义。
所以,我们先关注退出信号,如果有异常了我们再去关注退出码。
WEXITSTATUS 宏用于查看进程的退出码,若非 0,提取子进程退出码。
WEXITSTATUS(status)
WIFEXITED 宏用于查看进程是否正常退出,如果是正常终止的子进程返回状态,则为真。
WIFEXITED(status)
waitpid
意义:
-
返回记录子进程内核的数据结构
-
Z->X
相关文章:

【Linux详解】进程等待 | 非阻塞轮询
引入: 为什么?是什么?怎么办 是什么? 进程等待是指父进程暂停自己的执行,直到某个特定的子进程结束或发生某些特定的事件。 为什么? 僵尸进程刀枪不入,不可被杀死,存在内存泄露…...

聊一下Maven打包的问题(jar要发布)
文章目录 一、问题和现象二、解决方法(1)方法一、maven-jar-pluginmaven-dependency-plugin(2)方法二、maven-assembly-plugin 一、问题和现象 现在的开发一直都是用spring boot,突然有一天,要自己开发一个…...

JavaScript中,正则表达式所涉及的api,解析、实例和总结
JS中正则的api包括以下: String#searchString#splitString#matchString#replaceRegExp#testRegExp#exec 1. String#search 查找输入串中第一个匹配正则的index,如果没有匹配的则返回-1。g修饰符对结果无影响 var string "abbbcbc"; var r…...
【计算机】同步/异步
同步/异步 在计算机科学和编程中,“同步”(Synchronization)是一种机制,用于协调不同进程或线程之间的操作,以避免竞态条件(race conditions)、死锁(deadlocks)和其他并…...

谈大语言模型动态思维流程编排
尽管大语言模型已经呈现出了强大的威力,但是如何让它完美地完成一个大的问题,仍然是一个巨大的挑战。 需要精心地给予大模型许多的提示(Prompt)。对于一个复杂的应用场景,编写一套完整的,准确无误的提示&am…...

工厂自动化相关设备工业一体机起到什么作用?
在当今的制造业领域,工厂自动化已成为提高生产效率、保证产品质量和降低成本的关键。在这一进程中,工业一体机作为一种重要的设备,发挥着不可或缺的作用。 工业一体机是自动化生产线上的控制中心。它能够整合和处理来自各个传感器、执行器和其…...

哈佛大学 || 概念空间中学习动态的涌现:探索隐藏能力
获取本文论文原文PDF,请在公众号【AI论文解读】留言:论文解读 今天主要看一个问题:在模型中的学习动态是如何涌现的。 在现代生成模型的研究与应用中,不断发现这些模型在处理训练数据时展现出了惊人的能力,这些能力很…...
Dockerfile打包部署常用操作
文章目录 1、Dockerfile部署java程序(jar包)1.1、创建Dockerfile1.2、将Dockerfile和要上传的jar包放到一个目录下,构建镜像1.3、创建启动容器 2、Dockerfile部署vue2.1、创建dockerfile文件2.2、将打包的dist文件放到dockerfile同文件目录下…...

ArcGIS:探索地理信息系统的强大功能与实际应用
ArcGIS是一款功能强大的地理信息系统(GIS)软件,由Esri公司开发。它广泛应用于各个领域,包括城市规划、环境保护、资源管理、交通运输等。作为一名长期使用ArcGIS的用户,我深感这款软件在数据分析、地图制作和空间信息管…...
Python 全栈体系【三阶】(二)
第一章 Django 五、模板 1. 概述 Django中的模板是指可以动态生成任何基于文本格式文件的技术(如HTML、CSS等)。 Django中内置了自己的模板系统,称为DTL(Django Template Language), Django模板语言。 2. 配置 settings.py中关于模板的…...
【VUE】 深入理解 Vue 动态路由:简介、实际开发场景与代码示例
深入理解 Vue 动态路由:简介、实际开发场景与代码示例 Vue.js 是一个用于构建用户界面的渐进式框架,它拥有丰富的生态系统,其中 Vue Router 是其官方的路由管理库。动态路由是 Vue Router 的一个强大特性,允许我们在应用运行时根…...
Linux x86_64平台指令替换函数 text_poke_smp/bp
文章目录 前言一、text_poke_early1.1 text_poke_early简介1.2 用途 二、text_poke_smp2.1 简介2.1.1 text_poke_smp函数2.2.2 stop_machine_text_poke简介2.2.3 text_poke函数 2.2 用途 三、text_poke_smp 内核hook 前言 Linux x86_64平台指令替换函数有两种类型:…...

海南云亿商务咨询有限公司口碑怎么样?
在数字化浪潮席卷全球的今天,电商行业正以前所未有的速度发展。抖音作为短视频领域的佼佼者,其电商功能更是为众多品牌和企业打开了全新的销售渠道。海南云亿商务咨询有限公司,作为抖音电商服务领域的佼佼者,正以其专业的服务和创…...

航空数据管控系统-②项目分析与设计:任务2:使用Git或SVN管理项目(可选任务,只介绍Git安装)
任务描述 1、安装Git 2、注册GitHub 3、配置本地库 4、配置远程库 5、使用Git管理项目 任务指导 分为以下几个部分完成: 学会Git的安装,帐号注册本地存储库的管理自己创建一个项目,项目名称为自己的名字,上传到代码仓库ÿ…...

【面试题】串联探针和旁挂探针有什么区别?
在网络安全领域中,串联探针和旁挂探针(通常也被称为旁路探针)是两种不同部署方式的监控设备,它们各自具有独特的特性和应用场景。以下是它们之间的主要区别: 部署方式 串联探针:串联探针一般通过网关或者…...

LeetCode42(接雨水)[三种解法:理解动态规划,双指针,单调栈]
接雨水 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。 这是一道困难题,难度确实有点层次.我们先来朴素思想走一波. 要求能接多少雨水,我们可以具化到每个硅谷,每个硅谷能存多少雨水,那么答案就是每个…...

STM32-ADC+DMA
本内容基于江协科技STM32视频学习之后整理而得。 文章目录 1. ADC模拟-数字转换器1.1 ADC模拟-数字转换器1.2 逐次逼近型ADC1.3 ADC框图1.4 ADC基本结构1.5 输入通道1.6 规则组的转换模式1.6.1 单次转换,非扫描模式1.6.2 连续转换,非扫描模式1.6.3 单次…...
代码随想录算法训练营第六十二天 | 108. 冗余连接、109. 冗余连接II、复习
108. 冗余连接 题目链接:https://kamacoder.com/problempage.php?pid1181 文档讲解:https://www.programmercarl.com/kamacoder/0108.%E5%86%97%E4%BD%99%E8%BF… 思路 从前向后遍历每一条边(因为优先让前面的边连上)࿰…...

昇思MindSpore学习笔记6-01LLM原理和实践--FCN图像语义分割
摘要: 记录MindSpore AI框架使用FCN全卷积网络理解图像进行图像语议分割的过程、步骤和方法。包括环境准备、下载数据集、数据集加载和预处理、构建网络、训练准备、模型训练、模型评估、模型推理等。 一、概念 1.语义分割 图像语义分割 semantic segmentation …...
【FFMPEG基础(一)】解码源码
学习分享 main函数decodetorgb32.h 文件decodetorgb32 .cpp文件 main函数 #include <QApplication> #include "decodetorgb32.h" int main(int argc, char *argv[]) {QApplication a(argc, argv);DecodeToRGB32 toRGB32;int restoRGB32.openVideo("../fi…...

接口测试中缓存处理策略
在接口测试中,缓存处理策略是一个关键环节,直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性,避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明: 一、缓存处理的核…...
vscode里如何用git
打开vs终端执行如下: 1 初始化 Git 仓库(如果尚未初始化) git init 2 添加文件到 Git 仓库 git add . 3 使用 git commit 命令来提交你的更改。确保在提交时加上一个有用的消息。 git commit -m "备注信息" 4 …...

为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?
在建筑行业,项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升,传统的管理模式已经难以满足现代工程的需求。过去,许多企业依赖手工记录、口头沟通和分散的信息管理,导致效率低下、成本失控、风险频发。例如&#…...

LeetCode - 394. 字符串解码
题目 394. 字符串解码 - 力扣(LeetCode) 思路 使用两个栈:一个存储重复次数,一个存储字符串 遍历输入字符串: 数字处理:遇到数字时,累积计算重复次数左括号处理:保存当前状态&a…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案
问题描述:iview使用table 中type: "index",分页之后 ,索引还是从1开始,试过绑定后台返回数据的id, 这种方法可行,就是后台返回数据的每个页面id都不完全是按照从1开始的升序,因此百度了下,找到了…...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放
简介 前面两期文章我们介绍了I2S的读取和写入,一个是通过INMP441麦克风模块采集音频,一个是通过PCM5102A模块播放音频,那如果我们将两者结合起来,将麦克风采集到的音频通过PCM5102A播放,是不是就可以做一个扩音器了呢…...
TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案
一、TRS收益互换的本质与业务逻辑 (一)概念解析 TRS(Total Return Swap)收益互换是一种金融衍生工具,指交易双方约定在未来一定期限内,基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...

微服务商城-商品微服务
数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...

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

处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的
修改bug思路: 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑:async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...