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

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方法

waitpidwait都是等待进程。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进程等待

前言&#xff1a;接着前面进程终止&#xff0c;话不多说我们进入Linux进程等待的学习&#xff0c;如果你还不了解进程终止建议先了解&#xff1a; Linux进程终止 本篇主要内容&#xff1a; 什么是进程等待 为什么要进行进程等待 如何进程等待 进程等待 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&#xff1b;configure配置模块&#xff1b;make编译无需安装&#xff1b;把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

情况&#xff1a;nginx代理java 请求 后端返回正常&#xff0c;但是经过nginx 时报502 经过多次对比其他接口发现可能是返回的请求头过大&#xff0c;导致nginx 报错&#xff1a;如下 2024/05/13 02:57:12 [error] 88#88: *3755 upstream sent too big header while reading r…...

面试集中营—Redis面试题

一、Redis的线程模型 Redis是基于非阻塞的IO复用模型&#xff0c;内部使用文件事件处理器&#xff08;file event handler&#xff09;&#xff0c;这个文件事件处理器是单线程的&#xff0c;所以Redis才叫做单线程的模型&#xff0c;它采用IO多路复用机制同时监听多个socket&a…...

关于使用git拉取gitlab仓库的步骤(解决公钥问题和pytho版本和repo版本不对应的问题)

先获取权限&#xff0c;提交ssh-key 虚拟机连接 GitLab并提交代码_gitlab提交mr-CSDN博客 配置完成上诉步骤之后&#xff0c;执行下列指令进行拉去仓库的内容 sudo apt install repo export PATHpwd/.repo/repo:$PATH python3 "实际路径"/repo init -u ssh://gitxx…...

Django图书馆综合项目-学习(2)

接下来我们来实现一下图书管理系统的一些相关功能 1.在书籍的book_index.html中有一个"查看所有书毂"的超链接按钮&#xff0c;点击进入书籍列表book_list.html页面. 这边我们使用之前创建的命名空间去创建超连接 这里的book 是在根路由创建的namespacelist是在bo…...

vue3+ts 获取input 输入框中的值

从前端input 输入框获取值&#xff0c;通过封装axios 将值传给后端服务 数据格式为json html <el-form> <el-form-item label"域名"><el-input v-model"short_url" style"width: 240px"type"text"placeholder&quo…...

Gin框架返回Protobuf类型:提升性能的利器

在构建高效、高性能的微服务架构时&#xff0c;数据序列化和反序列化的性能至关重要。Protocol Buffers&#xff08;简称Protobuf&#xff09;作为一种轻量级且高效的结构化数据存储格式&#xff0c;已经在众多领域得到广泛应用。Gin框架作为Go语言中流行的Web框架&#xff0c;…...

HTML满屏漂浮爱心

目录 写在前面 满屏爱心 代码分析 系列推荐 写在最后 写在前面 小编给大家准备了满屏漂浮爱心代码&#xff0c;一起来看看吧~ 满屏爱心 文件heart.svg <svg xmlns"http://www.w3.org/2000/svg" width"473.8px" height"408.6px" view…...

爬虫应该选择住宅ip代理还是数据中心代理?

住宅代理 住宅代理是互联网服务提供商 (ISP) 提供的 IP 地址&#xff0c;它们是附加到实际物理位置的真实IP地址。住宅代理允许用户通过目标区域内的真实IP地址连接到互联网。 数据中心代理 数据中心代理是指是使用数据中心拥有并管理IP的代理&#xff0c;IP地址来源于数据中…...

百面算法工程师目录 | 深度学习目标检测、语义分割、分类上百种面试问答技巧

本文给大家带来的百面算法工程师是深度学习面试目录大纲&#xff0c;文章内总结了常见的提问问题&#xff0c;旨在为广大学子模拟出更贴合实际的面试问答场景。在这篇文章中&#xff0c;可以点击题目直达问题答案处&#xff0c;方便查找问题寻找答案。节约大家的时间。通过对这…...

Java中Maven的依赖管理

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

Github新手入门使用方法

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

期权隐含波动率到底是什么意思?

今天期权懂带你了解期权隐含波动率到底是什么意思&#xff1f;期权隐含波动率解析。通俗的说&#xff0c;期权隐含波动率是在期权市场中买家和卖家对于&#xff0c;某一期权合约价格变动幅度大小的判断。 期权隐含波动率到底是什么意思&#xff1f; 隐含波动率是根据期权市场价…...

28、Flink 为管理状态自定义序列化

为管理状态自定义序列化 a&#xff09;概述 对状态使用自定义序列化&#xff0c;包含如何提供自定义状态序列化程序、实现允许状态模式演变的序列化程序。 b&#xff09;使用自定义状态序列化程序 注册托管 operator 或 keyed 状态时&#xff0c;需要 StateDescriptor 来指…...

【强训笔记】day17

NO.1 思路&#xff1a;用一个字符串实现&#xff0c;stoi函数可以转化为数字并且去除前导0。 代码实现&#xff1a; #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解析的数据类型可能含有不同数据结构的处理方式

最近做一个需求&#xff0c;各种业务消息都会往我的消息队列中写各种类型的数据&#xff0c;服务端需要接受各种不同的参数然后转换为本地数据结构&#xff0c;Go语言不确定上游传过来的数值是什么类型&#xff0c;然后又下面四种解决方案。 1. 类型断言和类型切换 func (Mis…...

电动汽车模型考虑多种需求响应及微网的虚拟电厂日前优化调度模型的MATLAB代码(含空调负荷)

MATLAB代码&#xff1a;含多种需求响应及电动汽车的微网/虚拟电厂日前优化调度 关键词&#xff1a;需求响应 空调负荷 电动汽车 微网优化调度 虚拟电厂调度 仿真平台&#xff1a;MATLABCPLEX 主要内容&#xff1a;代码主要做的是一个微网/虚拟电厂的日前优化调度模型&#…...

一键迁移方案:OpenClaw配置备份与Qwen3-4B模型快速恢复

一键迁移方案&#xff1a;OpenClaw配置备份与Qwen3-4B模型快速恢复 1. 为什么需要配置迁移方案 上周我的开发机突然硬盘故障&#xff0c;导致所有OpenClaw配置丢失。重新配置飞书通道、模型连接和技能库花了大半天时间。这次教训让我意识到&#xff1a;自动化工具的配置本身也…...

VS2019+CMake实战:Super4PCS点云配准从源码编译到运行全流程指南

VS2019CMake实战&#xff1a;Super4PCS点云配准从源码编译到运行全流程指南 在三维视觉和机器人领域&#xff0c;点云配准一直是核心难题之一。Super4PCS算法作为4PCS的改进版本&#xff0c;以其在低重叠率点云上的优异表现&#xff0c;成为工业检测和SLAM系统中的热门选择。本…...

MPL115A2气压传感器驱动开发与嵌入式I²C实践

1. MPL115A2气压传感器技术解析与嵌入式驱动开发实践MPL115A2是由NXP&#xff08;原Freescale&#xff09;推出的一款高精度、低功耗、IC接口的绝对气压传感器&#xff0c;专为消费电子和工业应用中的海拔高度测量、天气监测及气压补偿等场景设计。该器件采用MEMS压阻式传感原理…...

智慧校园系统怎么选?看懂这 5 个核心功能再决定不迟

✅作者简介&#xff1a;合肥自友科技 &#x1f4cc;核心产品&#xff1a;智慧校园系统(包括教工管理、学工管理、教务管理、考务管理、后勤管理、德育管理、资产管理、公寓管理、实习管理、就业管理、离校管理、科研平台、档案管理、学生平台等26个子平台) 。公司所有人员均有多…...

蓝桥杯19723分布式队列

import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner innew Scanner(System.in);int nin.nextInt();int l[]new int[n];//记录每行的长度while (in.hasNext()){String sin.next();if(s.equals("add")){int xin.nextInt();…...

别再只会apt autoremove了!dpkg报错Sub-process returned error code (1)的5种修复姿势全解析

深度解析dpkg报错Sub-process returned error code (1)的5种专业修复策略 当你正在Ubuntu或Debian系统上安装某个关键软件包时&#xff0c;突然终端弹出一条刺眼的错误信息&#xff1a;"Sub-process /usr/bin/dpkg returned an error code (1)"。这不是普通的警告&am…...

如何高效构建Steam游戏DRM解除自动化解决方案:开源框架技术实现

如何高效构建Steam游戏DRM解除自动化解决方案&#xff1a;开源框架技术实现 【免费下载链接】Steam-auto-crack Steam Game Automatic Cracker 项目地址: https://gitcode.com/gh_mirrors/st/Steam-auto-crack Steam游戏DRM解除自动化解决方案为技术爱好者提供了一套完整…...

基于光伏出力利用率的电动汽车充电站能量调度策略:动态评估充放电灵活性,优化准入规则与电价制定...

考虑光伏出力利用率的电动汽车充电站能量调度策略。 程序注释非常非常详细 针对间歇性能源利用的问题&#xff0c;构建电动汽车的充放电灵活度指标&#xff0c;用以评估电动汽车参与光伏充电站能量调度的能力&#xff1b; 令充电站在饥饿模式或饱和模式下运行&#xff0c;并根据…...

新手避坑指南:用RT-Thread Studio和星火一号,5分钟搞定AHT10温湿度采集与阿里云MQTT上传

星火一号开发板实战&#xff1a;5分钟完成AHT10温湿度采集与阿里云MQTT上云全流程 第一次拿到星火一号开发板时&#xff0c;看着板载的AHT10温湿度传感器和WiFi模块&#xff0c;我脑海中立刻浮现出一个完整的物联网场景&#xff1a;实时监测环境数据并上传到云端。但真正动手时…...