进程控制(二) 进程等待与进程替换

news2025/3/1 11:08:31

目录

一、进程等待

理解进程等待

演示进程等待

获取进程的status

进程等待接口讲解

wait用法​

waitpid用法

等待多进程

基于非阻塞调用的轮询式检测

二、进程程序替换

excel接口

程序替换演示

单进程程序替换

多进程程序替换

程序替换原理

单进程程序替换

多进程程序替换

程序替换函数总结

两个小结论:


一、进程等待

理解进程等待

父进程等创建子进程是为了完成某种任务,当子进程完成任务后父进程需要回收子进程,所以父进程等待子进程退出的过程就叫做进程等待!

所以进程等待可能等待的不止是硬件资源,也有可能是在等待软件资源(进程)!

进程等待的必要性
1. 解决子进程处于僵尸状态带来的内存泄漏问题(博客: 进程概念和进程状态)
2. 父进程需要知道子进程执行任务的结果(退出码与终止信号)

演示进程等待

获取进程的status

下面要介绍的两个系统调用 wait 和 waitpid 都有1个参数,参数名字就叫做 status, status是一个输出型参数,也就是由用户定义,调用完wait/waitpid之后,status会被设置成等待的子进程的退出状态!如果不关心子进程的退出状态信息,传空指针即可!

但是status不能看成一个整体去分析,而是要如下分析:

status是一个整形变量,我们只研究较低的16个比特位,16个比特位被划分成3部分,最低的7表示终止信号, 较高的8位表示退出码,中间的1个比特位是core dump(后续博客讲解)

ps: 当进程异常退出时,也就是终止信号为非0时,进程的退出码也就没有了任何意义!

获取进程的退出码和终止信号有两种方式:

1. 位操作

exitCode = (status >> 8) & 0xFF; //退出码
exitSignal = status & 0x7F;      //终止信号

2. 系统定义的宏常量

exitNormal = WIFEXITED(status);  //是否正常退出
exitCode = WEXITSTATUS(status);  //获取退出码
进程等待接口讲解
wait用法

wait等待的是任意一个进程, 等待成功返回等待进程pid, 等待失败,返回-1

父进程睡眠了10秒,而子进程运行了5秒就退出了,所以当子进程退出后,父进程还在运行,没有回收子进程,所以子进程先处于僵尸状态,5秒之后,父进程调用wait接口,回收子进程,子进程从僵尸状态变为死亡状态(瞬时状态), 然后就查不到子进程了!

如果子进程还没有退出,那么父进程就要进行阻塞式等待(停留在调用wait接口那行代码),直到子进程退出,回收资源!

waitpid用法

waitpid等待的是特定的进程! 如果pid传-1,等待的就是任意一个进程!

位操作获取退出码与终止信号

正常退出

异常退出

程序运行时出问题

用户发信号终止
宏常量获取退出码与终止信号
等待多进程

进程是按照0,1,2···的编号创建的,但是调度顺序完全是由调度器决定的!

下图演示了用户是如何通过系统调用接口获得子进程退出的状态!

基于非阻塞调用的轮询式检测

接下来就要介绍第三个参数了,第三个参数是让我们选择等待的方式, 进程等待分两种:

1. 阻塞等待, 传0

2. 非阻塞等待,传 WNOHANG, WNOHANG 意思是 wait no hang, 意思是等待时不要夯住了!

阻塞等待: 在父进程调用 wait/waitpid接口时,子进程如果不退出,wait/waipid就不返回,这就注定了父进程在等待子进程时候什么事情都做不了,只能等!

非阻塞等待: 在父进程等待子进程时如果子进程没有退出,wait/waitpid 不阻塞,也就是wait/waitpid立即返回,这就是非阻塞等待,而非阻塞注定了 wait/waitpid要重复调用(轮询的过程), 不断的查看子进程的状态! 

非阻塞等待的优势就是父进程在等待子进程的过程中可以做一做自己的事情!

非阻塞等待waipid的返回值:

1. >0 等待成功

2. ==0 子进程还没有退出, waitpid返回了

3. <0 等待失败(等待失败一般是因为把要等待的进程pid传错了)

以下代码是演示基于非阻塞的的轮巡式检测以及父进程在等待期间做其他事情!

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <unistd.h>
  4 #include <sys/types.h>
  5 #include <sys/wait.h>
  6 
  7 #define TASK_NUM 5  
  8 
  9 typedef void (*task_t)(); //函数指针类型
 10 
 11 
 12 void download()
 13 {
 14     printf("A download task is running!\n");
 15 }
 16 void printlog()
 17 {
 18     printf("A prinlog task is running!\n");
 19 }
 20 void show()
 21 {
 22     printf("A show task is running!\n");
 23 }                                                                                                                                                        
 24 ///
 25 
 26 void InitTask(task_t tasks[], int num)
 27 {                                                                                                                                                        
 28     for(int i = 0; i < num; i++) tasks[i] = NULL;
 29 }
 30 
 31 int AddTask(task_t tasks[], task_t t)
 32 {
 33     for(int i = 0; i  < TASK_NUM; i++)
 34     {
 35         if(tasks[i] == NULL)
 36         {
 37             tasks[i] = t;
 38             return 1;
 39         }
 40     }
 41     return 0;
 42 }
 43 
 44 void ExecuteTask(task_t tasks[], int num)
 45 {
 46     for(int i = 0; i < num; i++)
 47     {
 48         if(tasks[i]) tasks[i]();
 49     }
 50 }
 51 
 52 void Worker(int cnt)
 53 {
 54     printf("I am child, pid: %d, cnt: %d\n", getpid(), cnt);                                                                                             
 55 }
 56 
 57 int main()
 58 {
 59     task_t tasks[TASK_NUM];
 60     InitTask(tasks, TASK_NUM);
 61     AddTask(tasks, download);
 62     AddTask(tasks, printlog);
 63     AddTask(tasks, show);
 64 
 65     pid_t id = fork();
 66     if(0 == id)
 67     {
 68         //child
 69         int cnt = 5;
 70         while(cnt)
 71         {
 72             Worker(cnt);
 73             sleep(2);
 74             cnt--;
 75         }
 76         exit(0);
 77     }
 78 
 79     //father
 80     int status = 0;
 81     while(1)  //非阻塞轮询检测
 82     {
 83         pid_t rid = waitpid(id, &status, WNOHANG);
 84         if(rid > 0)
 85         {
 86             //wait sucess, child quit already
 87             printf("child quit sucess, exit code: %d, exit signal:%d\n", (status>>8)&0xFF, status&0x7F);
 88             break;
 89         }
 90         else if(0 == rid)
 91         {
 92             //wait sucess, but child not quit
 93             //printf("child is alive, wait again, father do other things...\n");
 94             ExecuteTask(tasks, TASK_NUM); //也可以在内部进行移除或者新增对应的任务!
 95         }
 96         else
 97         {
 98             //wait failed, child unknown
 99             printf("wait failed!\n");
100             break;
101         }
102         sleep(1);
103     }
104     return 0;
105 }

二、进程程序替换

我们目前所创建的所有子进程执行的代码都是父进程代码的一部分!如果想让子进程执行新的程序,执行新的代码和访问全新的数据,不再和父进程有关系,这就要依靠程序替换了!

excel接口

1.execl是一个用于程序替换的接口,可执行程序本质就是磁盘上的二进制文件

2.所有的程序替换接口都是以exec开头的,exec是execute的简写execl中的 l 代表传参方式是list列表方式

3. 第一个参数传的是文件的路径+文件名,保证找到文件!

后面的参数含义是如何执行这个可执行程序,命令行下如何运行可执行程序,就如何传参!

最后一个参数必须是NULL, 表示传参完成

程序替换演示

单进程程序替换


但是我们发现excel后续的printf语句没有执行,在下面讲解程序替换原理我们再详谈!

多进程程序替换

程序替换原理

单进程程序替换

哪一个进程调用execl等程序替换的系统调用,系统就会将要替换的磁盘上文件的代码和数据直接覆盖到子进程的代码段和数据段!cpu再调度运行时,就执行的是新程序的代码和数据了!

程序替换过程并没有创建新进程,进程的pid不会改变!

多进程程序替换

多进程程序替换相比单进程程序替换无非就是多了写时拷贝,和之前写时拷贝不同的是,此处不只要写时拷贝数据,还要写时拷贝代码,因为子进程发生程序替换,代码和数据都会被覆盖掉,所以也要写时拷贝代码,否则父子进程独立性就无法保证了!

Q:程序替换之后,子进程如何知道从新的程序的最开始执行,如何知道最开始在哪?

A: 编译形成可执行程序,可执行程序的代码和数据是有一定的格式的(ELF格式), 在可执行程序的开始有一个字段entry, 里面保存了可执行程序的入口地址!发生程序替换时,系统调用接口会读取entry, 并且将entry内容填充到当前进程的eip寄存器中(之前博客已经讲过进程切换,eip寄存器只有1个,但是eip的内容可以有多份,详见博客)  进程优先级与环境变量-CSDN博客 

excel之后的printf没有执行的原因

在调用execl的时候发生了程序替换,所以最后运行我们的程序把系统指令调了起来,但是会发现,并没有输出execl后续的printf代码! 是因为发生了程序替换,调用完execl接口后,代码和数据都被覆盖了,所以后续的printf代码都被覆盖了, 同时程序计数器内容也被改变了!所以程序替换一旦成功,后续代码就没有机会再执行了,只有当程序替换失败了,才会有返回值,才会执行后续代码!

小细节:

excel第一个参数也就是路径+文件名是对的,但是程序运行方法传递错了,但是程序替换还是成功的,这是因为系统接口代码的健壮性(鲁棒性)比较强,他根据第一个参数已经能够识别是ls了,所以最终也能程序替换成功,但是建议我们在写的时候按标准来!!!

程序替换函数总结

这些都是程序替换的函数,都封装了execve这个系统调用!! 之所以设计这么多接口是为了满足各种调用的场景!

任何程序替换函数都要解决两个问题:

a.必须先站到这个可执行程序

b.必须告诉exec*,怎么执行

file:文件名,p: PATH,  表示execlp会自动的去环境变量PATH中根据file去寻找可执行程序!

v表示vector, 是数组的意思,除了可变参数接收选项之外,argv[ ]指针数组也可以存放选项!

file:文件名,p: PATH,  表示execlp会自动的去环境变量PATH中根据file去寻找可执行程序!

传递系统环境变量

传递自定义环境变量(默认覆盖式传递)

上述演示的都是程序替换系统指令,下面演示一下程序替换自己的可执行程序

替换C++程序

替换脚本语言

替换python

指令运行起来是进程,我们自己写的可执行程序运行起来也是进程,因为exec*是进程程序替换的接口,所以都能替换,系统大于一切!

两个小结论

1.程序替换时,子进程对应的环境变量是可以直接从父进程来的

在bash中导入自定义的环境变量,最后发现子进程(fork创建孙子进程)和孙子进程(发生程序替换)都有自定义的环境变量

而在myprocess.c中导入自定义环境变量,父进程bash看不到环境变量,但是fork创建的子进程可以继承!

2.环境变量被子进程继承下去是一种默认行为,不受程序替换的影响, 因为程序替换,只替换新进程的代码和数据,环境变量不会被替换!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1504258.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Web Worker:JavaScript的后台任务解决方案

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

阿里云服务器ECS u1实例2核4G5M带宽优惠价199元/年性能测评

阿里云服务器ECS u1实例&#xff0c;2核4G&#xff0c;5M固定带宽&#xff0c;80G ESSD Entry盘优惠价格199元一年&#xff0c;性能很不错&#xff0c;CPU采用Intel Xeon Platinum可扩展处理器&#xff0c;购买限制条件为企业客户专享&#xff0c;实名认证信息是企业用户即可&a…

每日OJ题_牛客HJ60 查找组成一个偶数最接近的两个素数

目录 牛客HJ60 查找组成一个偶数最接近的两个素数 解析代码 牛客HJ60 查找组成一个偶数最接近的两个素数 查找组成一个偶数最接近的两个素数_牛客题霸_牛客网 解析代码 #include <cmath> #include <iostream> using namespace std; bool isPrime(int n) {for (…

STM32H750片外QSPI启动配置简要

STM32H750片外QSPI启动配置简要 &#x1f4cd;参考信息源&#xff1a;《STM32H750片外Flash启动(W25Q64JVSIQ)》&#x1f516;本例程基于Keil MDk开发平台。&#x1f341;配置框架&#xff1a; ✨为什么使用要使用QSPI启动方式 不管对于STM32H7系列单片机&#xff0c;还是其他…

BC134 蛇形矩阵

一&#xff1a;题目 二&#xff1a;思路分析 2.1 蛇形矩阵含义 首先&#xff0c;这道题我们要根据这个示例&#xff0c;找到蛇形矩阵是怎么移动的 这是&#xff0c;我们可以标记一下每次移动到方向 我们根据上图可以看出&#xff0c;蛇形矩阵一共有两种方向&#xff0c;橙色…

解决ChatGPT发送消息没有反应

ChatGPT发消息没反应 今天照常使用ChatGPT来帮忙码代码&#xff0c;结果发现发出去的消息完全没有反应&#xff0c;即不给我处理&#xff0c;也没有抱任何的错误&#xff0c;按浏览器刷新&#xff0c;看起来很正常&#xff0c;可以查看历史对话&#xff0c;但是再次尝试还是一…

一分钟了解遥感中卫星、传感器、波段及数据之间的关系

感是利用卫星、飞机或其他载具上的传感器对地球表面进行观测和测量的科学技术。以下是一些常见的遥感相关术语&#xff1a; 卫星&#xff08;Satellite&#xff09;&#xff1a;在遥感中&#xff0c;卫星是指绕地球轨道运行的人造卫星&#xff0c;其主要任务是携带各种传感器从…

qt+opencv人脸人眼检测识别

项目运行涉及到opencv库&#xff0c;以及haarcascade_frontalface_default.xml和haarcascade_eye_tree_eyeglasses.xml。qt配置opencv可见先前文章&#xff0c;另外这两份OpenCV 中用于眼睛检测的级联分类器xml文件&#xff0c;是我在网上下载的。 把要使用到的文件都放到当前…

指针的学习5

目录 sizeof和strlen的区别 sizeof strlen 数组和指针笔试题解析 一维数组 字符数组 二维数组 指针运算笔试题解析 题目1&#xff1a; 题目2&#xff1a; 题目3&#xff1a; 题目4&#xff1a; 题目5&#xff1a; 题目6&#xff1a; 题目7&#xff1a; sizeof和…

Unity Shader实现UI流光效果

效果&#xff1a; shader Shader "UI/Unlit/Flowlight" {Properties{[PerRendererData] _MainTex("Sprite Texture", 2D) "white" {}_Color("Tint", Color) (1, 1, 1, 1)[MaterialToggle] PixelSnap("Pixel snap", float…

一个爬虫自动化数据采集的故事~

目录 一、原文二、故事前半段背景内容三、正经的讲点DrissionPage知识四、故事的收尾 一、原文 原文来自一个爬虫自动化数据采集的故事~ , 建议点击链接看文章末尾的视频笔者不擅长自动化&#xff0c;一个小小故事分享给大家&#xff0c;仅个人观点 二、故事前半段背景内容 …

【C++】c++入门之递归上 数值类

文章目录 前言一、 递归1.1 基本概念1.2 递归的过程1.3 使用场景 二、例题讲解问题一&#xff1a;1002 - 编程求解123...n问题二&#xff1a;1241 - 角谷猜想问题三&#xff1a;1108 - 正整数N转换成一个二进制数问题四&#xff1a;1088 - 求两个数M和N的最大公约数 三、练习问…

【扩散模型系列3】DiT开源项目

文章目录 DiT原始项目Fast-DiT readmeSamplingTraining训练之前的准备训练DiTPyTorch 训练结果改进训练效果 Evaluation (FID, Inception Score, etc.) 总结 DiT原始项目 该项目仅针对DiT训练&#xff0c;并未包含VAE 的训练 项目地址 论文主页 Fast-DiT readme 该项目仅针…

【毕业】 医药药店销售管理系统

1、引言 设计结课作业,课程设计无处下手&#xff0c;网页要求的总数量太多&#xff1f;没有合适的模板&#xff1f;数据库&#xff0c;java&#xff0c;python&#xff0c;vue&#xff0c;html作业复杂工程量过大&#xff1f;毕设毫无头绪等等一系列问题。你想要解决的问题&am…

OpenJDK 目前主要发展方向

Loom&#xff1a;得赶紧解决 synchronized pin 线程的问题&#xff08;据说 Java 23 会解决&#xff0c;现在有预览版&#xff09;。各个 Java 库需要改造原来使用 ThreadLocal 的方式&#xff1a;如果是为了穿参数&#xff0c;则可以使用 ScopedLocal&#xff1b;如果是对象池…

【leetcode热题】寻找旋转排序数组中的最小值 II

难度&#xff1a; 困难通过率&#xff1a; 38.7%题目链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 题目描述 假设按照升序排序的数组在预先未知的某个点上进行了旋转。 ( 例如&#xff0c;数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。 请找出其中最小的…

激光打标机红光与激光不重合:原因及解决方案

激光打标机红光和激光不在一个位置的问题可能由多种原因导致。以下是一些可能的原因和解决方法&#xff1a; 1. 激光器光路调整不当&#xff1a;激光器光路调整不当会导致激光束偏移&#xff0c;从而使红光与激光不重合。解决方法是重新调整激光器的光路&#xff0c;确保激光束…

Session登陆实践

Session登陆实践 Session登录是一种常见的Web应用程序身份验证和状态管理机制。当用户成功登录到应用程序时&#xff0c;服务器会为其创建一个会话&#xff08;session&#xff09;&#xff0c;并在会话中存储有关用户的信息。这样&#xff0c;用户在与应用程序交互的整个会话…

C语言逗号运算符(,)

在C语言中&#xff0c;逗号运算符&#xff08;,&#xff09;用于在表达式中分隔多个子表达式&#xff0c;并按照从左到右的顺序依次计算这些子表达式。逗号运算符的运算结果是最后一个子表达式的值。 逗号运算符的底层行为是依次计算每个子表达式&#xff0c;并将每个子表达式…

SSM框架,MyBatis-Plus的学习(下)

条件构造器 使用MyBatis-Plus的条件构造器&#xff0c;可以构建灵活高效的查询条件&#xff0c;可以通过链式调用来组合多个条件。 条件构造器的继承结构 Wrapper &#xff1a; 条件构造抽象类&#xff0c;最顶端父类 AbstractWrapper &#xff1a; 用于查询条件封装&#xf…