【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…...

Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动
一、前言说明 在2011版本的gb28181协议中,拉取视频流只要求udp方式,从2016开始要求新增支持tcp被动和tcp主动两种方式,udp理论上会丢包的,所以实际使用过程可能会出现画面花屏的情况,而tcp肯定不丢包,起码…...
Admin.Net中的消息通信SignalR解释
定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...

MongoDB学习和应用(高效的非关系型数据库)
一丶 MongoDB简介 对于社交类软件的功能,我们需要对它的功能特点进行分析: 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具: mysql:关系型数据库&am…...

Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级
在互联网的快速发展中,高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司,近期做出了一个重大技术决策:弃用长期使用的 Nginx,转而采用其内部开发…...

CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)
漏洞概览 漏洞名称:Apache Flink REST API 任意文件读取漏洞CVE编号:CVE-2020-17519CVSS评分:7.5影响版本:Apache Flink 1.11.0、1.11.1、1.11.2修复版本:≥ 1.11.3 或 ≥ 1.12.0漏洞类型:路径遍历&#x…...

pgsql:还原数据库后出现重复序列导致“more than one owned sequence found“报错问题的解决
问题: pgsql数据库通过备份数据库文件进行还原时,如果表中有自增序列,还原后可能会出现重复的序列,此时若向表中插入新行时会出现“more than one owned sequence found”的报错提示。 点击菜单“其它”-》“序列”,…...

【汇编逆向系列】六、函数调用包含多个参数之多个整型-参数压栈顺序,rcx,rdx,r8,r9寄存器
从本章节开始,进入到函数有多个参数的情况,前面几个章节中介绍了整型和浮点型使用了不同的寄存器在进行函数传参,ECX是整型的第一个参数的寄存器,那么多个参数的情况下函数如何传参,下面展开介绍参数为整型时候的几种情…...

dvwa11——XSS(Reflected)
LOW 分析源码:无过滤 和上一关一样,这一关在输入框内输入,成功回显 <script>alert(relee);</script> MEDIUM 分析源码,是把<script>替换成了空格,但没有禁用大写 改大写即可,注意函数…...
达梦使用存储过程实现删除重复记录、判断并添加主键和自增列的逻辑
在达梦数据库中,要确保主键的唯一性约束,可以在存储过程的最前面添加删除重复记录的逻辑。以下是一个完整的存储过程,包含删除重复记录、判断并添加主键和自增列的逻辑: 存储过程示例 -- 切换到指定模式;schema_name 是目标模…...

【JMeter】后置处理器 - 提取器
文章目录 概览边界提取器正则提取器JSON提取器 概览 CSS/JQuery提取器;给网页使用JSON提取器:给JSON数据使用★边界提取器:给字符串使用★正则表达式提取器:更加高级的字符使用★Xpath提取器:给网页使用 边界提取器…...