Linux进程控制——Linux进程等待
前言:接着前面进程终止,话不多说我们进入Linux进程等待的学习,如果你还不了解进程终止建议先了解:
Linux进程终止
本篇主要内容:
什么是进程等待
为什么要进行进程等待
如何进程等待
进程等待
- 1. 进程等待的概念
- 2. 进程等待必要性
- 3. 进程等待的方法
- 3.1 wait方法
- 3.2 waitpid方法
- 4. 获取子进程status
- 获取子进程退出信息
- 5. waitpid的第三个参数options
- 6. 总结拓展
1. 进程等待的概念
首先在开始之前我们提个问题,到底什么是进程等待?
进程等待的概念:
- 我们通常说的进程等待其实是通过wait/waitpid的方式,让父进程(一般)对子进程进行资源回收的等待过程,父进程必须等待这个子进程结束后,处理它的代码和数据!
2. 进程等待必要性
在了解完进程等待的概念后,新的问题出现了,我们为什么要进行进程等待,进程等待的必要性是什么?
进程等待必要性:
- 若子进程退出,而父进程对它不管不顾,就可能造成‘僵尸进程’的问题,进而造成内存泄漏。
- 进程一旦变成僵尸状态,那就刀枪不入,“杀人不眨眼”的kill -9 也无能为力,谁也没有办法杀死一个已经死去的进程。
- 父进程创建子进程的目的是为了让子进程协助自己完成任务的,而父进程需要知道子进程将任务完成得如何。这就需要通过进程等待的方式,获取子进程的退出信息。
3. 进程等待的方法
3.1 wait方法
我们可以通过系统调用来等待进程:wait
函数
wait
等待任意一个子进程的退出,如果等待成功他将返回子进程的pid,失败则返回-1
我们就用一段代码来看看wait
:
#include<stdio.h>#include<unistd.h>#include<stdlib.h>#include<sys/types.h>#include<sys/wait.h>void Worker(){int cnt = 5;while(cnt){printf("i am child, pid: %d, ppid: %d, cnt: %d\n", getpid(), getppid(), cnt--);sleep(1);}}int main(){ pid_t id = fork();if(id == 0){// childWorker();exit(0);}else{sleep(10);// fatherpid_t rid = wait(NULL);if(rid == id){printf("wait success, pid: %d\n", getpid());}}return 0;}
进程等待:wait函数
我们通过视频发现:进程等待是可以回收子进程僵尸状态的
然后我们将父进程sleep()
取消,看看在子进程退出之前父进程在干什么
#include<stdio.h>#include<unistd.h>#include<stdlib.h>#include<sys/types.h>#include<sys/wait.h>void Worker(){int cnt = 5;while(cnt){printf("i am child, pid: %d, ppid: %d, cnt: %d\n", getpid(), getppid(), cnt--);sleep(1);}}int main(){ pid_t id = fork();if(id == 0){// childWorker();exit(0);}else{// fatherprintf("wait before:\n");pid_t rid = wait(NULL);printf("wait after:\n");if(rid == id){printf("wait success, pid: %d\n", getpid());}sleep(10);}return 0;}
观察父进程等待过程
通过这个视频我们又能发现两个进程一起运行,但是在子进程没有退出之前,父进程一直在wait上等待,并且并没有出现子进程僵尸状态而是直接回收了。
结论:如果子进程根本就没有退出,父进程必须在wait上进行阻塞等待。直到子进程僵尸,wait自动回收返回。
3.2 waitpid方法
waitpid
和wait
都是等待进程。waitpid
可以指定等待一个进程,且有三个参数
4. 获取子进程status
父进程想要知道子进程的退出信息,也就是退出码和退出信号,就要用到输出型参数status
- wait和waitpid,都有一个status参数,该参数是一个输出型参数,由操作系统填充。
- 如果传递NULL,表示不关心子进程的退出状态信息。
- 否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程。
int main(){ pid_t id = fork();if(id == 0){// childWorker();exit(10); // 设置成10方便观察现象}else{// fatherprintf("wait before:\n");int status = 0;pid_t rid = waitpid(id, &status, 0);printf("wait after:\n");if(rid == id){printf("wait success, pid: %d, status: %d\n", getpid(), status);}}return 0;}
我明明将
exit
的退出结果设置成10,但是为什么他的status会是2560呢?
- 其实status不能简单的当作整形来看待,可以当作位图来看待,具体细节如下图(只研究status低16比特位)
因此我们在研究status时,不能整体使用status!!!
获取子进程退出信息
因为我们知道了status不能整体使用因此我们要进行位操作:
exit sig: status&0x7f //获取信号exit code: (status>>8)&0xff //获取退出结果
- 当我们的程序异常了,exit code 将无任何意义
- exit sig : 0则代表没有收到信号
- 手动杀掉子进程也会获取到信号
但是如果我们每次提取退出信息都要使用繁琐的位运算,这很不方便,因此系统给我们做了一个简单的封装
status:
- WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
- WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)
int main()
{pid_t id = fork();if(id == 0){// childWorker();exit(1);}else{// sleep(10);// fatherprintf("wait before:\n");//pid_t rid = wait(NULL); int status = 0; pid_t rid = waitpid(id, &status, 0); printf("wait after:\n");if(rid == id) { // 不能对status整体使用//printf("wait success, pid: %d, rpid: %d, exit sig: %d, exit code: %d\n",getpid(), rid, status&0x7f, (status>>8)&0xff);if(WIFEXITED(status)){printf("child process normal quit, exit code: %d\n", WEXITSTATUS(status));} else{ printf("child process quit exept!!!\n");}}} return 0;
}
当我们要获取多个进程的调度信息时,我们给每个进程都要一个编号,我们来观察一下进程是怎样调度的
void Worker(int number)
{int cnt = 5;while(cnt){printf("i am child, pid: %d, ppid: %d, cnt: %d, number: %d\n", getpid(), getppid(), cnt--, number);sleep(1);}
}
const int n = 10;int main()
{for(int i = 0; i < n; i++){pid_t id = fork();if(id == 0) { Worker(i); exit(i);} } // 等待多个子进程 for(int i = 0; i < n; i++) { int status = 0; pid_t rid = waitpid(-1, &status, 0); //pid > 0, -1:等待任意一个进程if(rid > 0){printf("wait child %d success, exit code: %d\n", rid, WEXITSTATUS(status));}}return 0;}
观察进程调度顺序
我们发现明明是按顺序创建的进程,但是在调度时却没有顺序可言,终止的时候也没有顺序,因为进程在调度完全由调度器说的算,所以进程调度的先后我们并不确定,这点在前面我们也提到过。
5. waitpid的第三个参数options
在使用waitpid的第三个参数时,前面我们提到设为0则是默认阻塞等待状态,必须等待子进程的退出,当时如果我们要做自己的事我们就不能使用0而是使用:WNOHANG
options:
- WNOHANG:若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程的ID。
- 0:默认的阻塞等待状态
**父进程在非阻塞等待时,因为子进程没有结束,就跑去做自己的事情了,但是又要继续等待,所以往往伴随着重复调,轮询,也就是基于非阻塞轮询的等待方案!
**
我们来直接验证一下非阻塞等待:
void Worker(int cnt)
{printf("i am child, pid: %d, cnt: %d\n", getpid(), cnt);sleep(1);
}
int main()
{pid_t id = fork();if(id == 0){// 子进程int cnt = 5;while(cnt){Worker(cnt);sleep(1);cnt--;}exit(1);}// 父进程while(1){int status = 0;pid_t rid = waitpid(id, &status, WNOHANG);if(rid > 0) {// 等待成功printf("wait success, child quit, exit code: %d, exit sig: %d\n", (status>>8)&0xff, status&0x7f);break;}else if(rid == 0){// 等待成功,但子进程并未退出printf("wait again, child alive, do other thing\n"); sleep(1);}else{// 等待失败printf("wait failed\n");break;} } return 0;}
我们可以看到非阻塞等待可以让我们的父进程在等待时,做自己的事!
6. 总结拓展
拓展一:父进程如何得知子进程的退出信息
父进程调用wait()/waitpid()
来获取子进程的退出信息,调用的接口就传入了一个status参数,而父进程中存在着一个statusp的指针。
而子进程在退出时,操作系统就会将退出信号和退出码写到子进程的PCD中
int exit_code;
int exit_signal
而退出信号和退出码将会写到这两个变量中,
当我们调用系统调用时,只需要将这两个变量组合写入到变量里
*statusp = (exit code<<8)| exit siganl
这样父进程就获取到了子进程的退出信息
拓展二:我们为什么不用全局变量获取子进程的退出信息而用系统调用?
这个就是因为进程具有独立性,父进程无法直接获得子进程的退出信息
总结:
进程等待确实非常有用,它既可以回收僵尸进程,避免造成内存泄漏,也能让父进程能够获取到子进程的退出信息,进程等待我们就先了解这么多,进程控制马上就到了我们的最后一步——进程替换,让我们来期待下一篇!
谢谢大家支持本篇到这里就结束了
相关文章:

Linux进程控制——Linux进程等待
前言:接着前面进程终止,话不多说我们进入Linux进程等待的学习,如果你还不了解进程终止建议先了解: Linux进程终止 本篇主要内容: 什么是进程等待 为什么要进行进程等待 如何进程等待 进程等待 1. 进程等待的概念2. 进…...

GPT-4o:融合文本、音频和图像的全方位人机交互体验
引言: GPT-4o(“o”代表“omni”)的问世标志着人机交互领域的一次重要突破。它不仅接受文本、音频和图像的任意组合作为输入,还能生成文本、音频和图像输出的任意组合。这一全新的模型不仅在响应速度上达到了惊人的水平,在文本、音频和图像理解方面也表现出色,给人带来了…...

灵活的静态存储控制器 (FSMC)的介绍(STM32F4)
目录 概述 1 认识FSMC 1.1 应用介绍 1.2 FSMC的主要功能 1.2.1 FSMC用途 1.2.2 FSMC的功能 2 FSMC的框架结构 2.1 AHB 接口 2.1.1 AHB 接口的Fault 2.1.2 支持的存储器和事务 2.2 外部器件地址映射 3 地址映射 3.1 NOR/PSRAM地址映射 3.2 NAND/PC卡地址映射 概述…...
nginx-rtmp
1.已经安装nginx;configure配置模块;make编译无需安装;把objs/nginx复制到已安装的宁目录下 ./configure --prefix/usr/local/nginx --add-module/usr/local/src/fastdfs-nginx-module/src --add-module/usr/local/src/nginx-rtmp-module-mas…...
nginx 代理java 请求报502
情况:nginx代理java 请求 后端返回正常,但是经过nginx 时报502 经过多次对比其他接口发现可能是返回的请求头过大,导致nginx 报错:如下 2024/05/13 02:57:12 [error] 88#88: *3755 upstream sent too big header while reading r…...

面试集中营—Redis面试题
一、Redis的线程模型 Redis是基于非阻塞的IO复用模型,内部使用文件事件处理器(file event handler),这个文件事件处理器是单线程的,所以Redis才叫做单线程的模型,它采用IO多路复用机制同时监听多个socket&a…...

关于使用git拉取gitlab仓库的步骤(解决公钥问题和pytho版本和repo版本不对应的问题)
先获取权限,提交ssh-key 虚拟机连接 GitLab并提交代码_gitlab提交mr-CSDN博客 配置完成上诉步骤之后,执行下列指令进行拉去仓库的内容 sudo apt install repo export PATHpwd/.repo/repo:$PATH python3 "实际路径"/repo init -u ssh://gitxx…...

Django图书馆综合项目-学习(2)
接下来我们来实现一下图书管理系统的一些相关功能 1.在书籍的book_index.html中有一个"查看所有书毂"的超链接按钮,点击进入书籍列表book_list.html页面. 这边我们使用之前创建的命名空间去创建超连接 这里的book 是在根路由创建的namespacelist是在bo…...
vue3+ts 获取input 输入框中的值
从前端input 输入框获取值,通过封装axios 将值传给后端服务 数据格式为json html <el-form> <el-form-item label"域名"><el-input v-model"short_url" style"width: 240px"type"text"placeholder&quo…...
Gin框架返回Protobuf类型:提升性能的利器
在构建高效、高性能的微服务架构时,数据序列化和反序列化的性能至关重要。Protocol Buffers(简称Protobuf)作为一种轻量级且高效的结构化数据存储格式,已经在众多领域得到广泛应用。Gin框架作为Go语言中流行的Web框架,…...

HTML满屏漂浮爱心
目录 写在前面 满屏爱心 代码分析 系列推荐 写在最后 写在前面 小编给大家准备了满屏漂浮爱心代码,一起来看看吧~ 满屏爱心 文件heart.svg <svg xmlns"http://www.w3.org/2000/svg" width"473.8px" height"408.6px" view…...
爬虫应该选择住宅ip代理还是数据中心代理?
住宅代理 住宅代理是互联网服务提供商 (ISP) 提供的 IP 地址,它们是附加到实际物理位置的真实IP地址。住宅代理允许用户通过目标区域内的真实IP地址连接到互联网。 数据中心代理 数据中心代理是指是使用数据中心拥有并管理IP的代理,IP地址来源于数据中…...
百面算法工程师目录 | 深度学习目标检测、语义分割、分类上百种面试问答技巧
本文给大家带来的百面算法工程师是深度学习面试目录大纲,文章内总结了常见的提问问题,旨在为广大学子模拟出更贴合实际的面试问答场景。在这篇文章中,可以点击题目直达问题答案处,方便查找问题寻找答案。节约大家的时间。通过对这…...

Java中Maven的依赖管理
依赖介绍 是指当前项目运行所需要的jar包,一个项目中可以引入多个依赖 配置 在pom.xml中编写<dependencies>标签 在<dependencies>中使用<dependency>引入标签 定义坐标的groupId、rtifactId、version 点击刷新按钮、引入新坐标 例如引入下…...

Github新手入门使用方法
**存在问题:**新手如何快速入门github,能够下载开源文件,并且修改后更新远程github仓库; 解决方案: 参考: http://www.360doc.com/content/24/0301/12/60419_1115656653.shtml https://blog.csdn.net/gongd…...

期权隐含波动率到底是什么意思?
今天期权懂带你了解期权隐含波动率到底是什么意思?期权隐含波动率解析。通俗的说,期权隐含波动率是在期权市场中买家和卖家对于,某一期权合约价格变动幅度大小的判断。 期权隐含波动率到底是什么意思? 隐含波动率是根据期权市场价…...
28、Flink 为管理状态自定义序列化
为管理状态自定义序列化 a)概述 对状态使用自定义序列化,包含如何提供自定义状态序列化程序、实现允许状态模式演变的序列化程序。 b)使用自定义状态序列化程序 注册托管 operator 或 keyed 状态时,需要 StateDescriptor 来指…...

【强训笔记】day17
NO.1 思路:用一个字符串实现,stoi函数可以转化为数字并且去除前导0。 代码实现: #include <iostream> #include<string> using namespace std;string s;int main() {cin>>s;for(int i0;i<s.size();i){if(s[i]%20) s[…...

平滑 3d 坐标
3d平滑 import torch import torch.nn.functional as F import numpy as np import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3Dclass SmoothOperator:def smooth(self, vertices):# 使用一维平均池化进行平滑vertices_smooth F.avg_pool1d(vertices.p…...
Go解析的数据类型可能含有不同数据结构的处理方式
最近做一个需求,各种业务消息都会往我的消息队列中写各种类型的数据,服务端需要接受各种不同的参数然后转换为本地数据结构,Go语言不确定上游传过来的数值是什么类型,然后又下面四种解决方案。 1. 类型断言和类型切换 func (Mis…...
Vim 调用外部命令学习笔记
Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...
生成xcframework
打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式,可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...
利用ngx_stream_return_module构建简易 TCP/UDP 响应网关
一、模块概述 ngx_stream_return_module 提供了一个极简的指令: return <value>;在收到客户端连接后,立即将 <value> 写回并关闭连接。<value> 支持内嵌文本和内置变量(如 $time_iso8601、$remote_addr 等)&a…...
逻辑回归:给不确定性划界的分类大师
想象你是一名医生。面对患者的检查报告(肿瘤大小、血液指标),你需要做出一个**决定性判断**:恶性还是良性?这种“非黑即白”的抉择,正是**逻辑回归(Logistic Regression)** 的战场&a…...

【机器视觉】单目测距——运动结构恢复
ps:图是随便找的,为了凑个封面 前言 在前面对光流法进行进一步改进,希望将2D光流推广至3D场景流时,发现2D转3D过程中存在尺度歧义问题,需要补全摄像头拍摄图像中缺失的深度信息,否则解空间不收敛…...

学校招生小程序源码介绍
基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码,专为学校招生场景量身打造,功能实用且操作便捷。 从技术架构来看,ThinkPHP提供稳定可靠的后台服务,FastAdmin加速开发流程,UniApp则保障小程序在多端有良好的兼…...
多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验
一、多模态商品数据接口的技术架构 (一)多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如,当用户上传一张“蓝色连衣裙”的图片时,接口可自动提取图像中的颜色(RGB值&…...

优选算法第十二讲:队列 + 宽搜 优先级队列
优选算法第十二讲:队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...

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

云原生安全实战:API网关Kong的鉴权与限流详解
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关(API Gateway) API网关是微服务架构中的核心组件,负责统一管理所有API的流量入口。它像一座…...