【Linux进程篇】Linux中的等待机制与替换策略

W...Y的主页 😊

代码仓库分享💕 

目录

​编辑

进程等待

进程等待必要性

进程等待的方法

wait方法

waitpid方法

获取子进程status

 阻塞与非阻塞

进程程序替换

替换原理

替换函数


进程等待

进程等待必要性

之前讲过,子进程退出,父进程如果不管不顾,就可能造成‘僵尸进程’的问题,进而造成内存泄漏。
另外,进程一旦变成僵尸状态,那就刀枪不入,“杀人不眨眼”的kill -9 也无能为力,因为谁也没有办法杀死一个已经死去的进程。
最后,父进程派给子进程的任务完成的如何,我们需要知道。如,子进程运行完成,结果对还是不对,或者是否正常退出。
父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息

上篇博客中,我们讲到进程退出时有两个非常重要的信息:退出信号和退出码。当我们进程退出时退出信息会被放入进程的PCB中进行保存,等待父进程的回收。

因为进程拥有独立性,所以我们想通过参数或返回值将信息转交给父进程那是不可能的,所以我们才要进行回收资源。

进程等待的方法

wait方法

返回值:
成功返回被等待进程pid,失败返回-1。
参数:
输出型参数,获取子进程退出状态,不关心则可以设置成为NULL

下面是测试子进程变僵尸后wait是否进行回收资源:

 

我们可以看出父进程可以将僵尸进程回收!!! 

waitpid方法

返回值:
当正常返回的时候waitpid返回收集到的子进程的进程ID;
如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;
参数:
pid:
Pid=-1,等待任一个子进程。与wait等效。
Pid>0.等待其进程ID与pid相等的子进程。
status:
WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)
options:
WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程的ID。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>int main()
{pid_t id = fork();if(id == 0){// childint cnt = 5;while(cnt){printf("Child is running, pid: %d, ppid: %d\n", getpid(), getppid());sleep(1);cnt--;}exit(1);}int status = 0;pid_t rid = waitpid(id, &status, 0); // 阻塞等待if(rid > 0){printf("wait success, rid: %d, status: %d\n", rid, status);}}

如果子进程已经退出,调用wait/waitpid时,wait/waitpid会立即返回,并且释放资源,获得子进程退出信息。
如果在任意时刻调用wait/waitpid,子进程存在且正常运行,则进程可能阻塞。
如果不存在该子进程,则立即出错返回。 

获取子进程status

wait和waitpid,都有一个status参数,该参数是一个输出型参数,由操作系统填充。
如果传递NULL,表示不关心子进程的退出状态信息。
否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程。
status不能简单的当作整形来看待,可以当作位图来看待,具体细节如下图(只研究status低16比特位): 

当我们的子进程exit(1)时,子进程的退出状态status为256。256代表什么呢?

任何进程最终执行状态我们可以使用两个数字具体表明情况

 我们为了直观一点,可以使用位操作,将退出信号和退出码分别打印出来:

printf("wait success, rid: %d, status: %d, exit signo: %d, exit code: %d\n", rid, status, status&0x7F, (status >> 8)&0xFF);

我们也可以使用宏来获取退出信号与退出码:

int main()
{pid_t id = fork();if(id == 0){// childint cnt = 5;while(cnt){printf("Child is running, pid: %d, ppid: %d\n", getpid(), getppid());sleep(1);cnt--;}exit(1);}int status = 0;pid_t rid = waitpid(id, &status, 0); // 阻塞等待if(WIFEXITED(status)){printf("wait success, rid: %d, status: %d, exit code: %d\n", rid, status, WEXITSTATUS(status));}}

 阻塞与非阻塞

在waitpid函数中,我们发现在上面的代码中,我们一直默认第三个参数为0。其就是对应的阻塞状态。而WNOHANG这个宏在第三个参数中可以进行填入,代表非阻塞等待。

阻塞等待时,父进程不能做任何事情,只能等待子进程变成僵尸进程后进行回收。而非阻塞等待可以轮转时进行行为。

非阻塞等待代码:

int main()
{pid_t id = fork();if(id == 0){// childint cnt = 5;while(cnt){printf("Child is running, pid: %d, ppid: %d\n", getpid(), getppid());sleep(1);cnt--;}exit(1);}int status = 0;pid_t rid = waitpid(id, &status, WNOHANG); while(1){if(rid > 0){printf("wait success, rid: %d, status: %d, exit code: %d\n", rid, status, WEXITSTATUS(status));break;}else if(rid == 0){printf("father say: child is running, do other thing\n");}else{perror("waitpid");break;}}return 0;
}

进程程序替换

替换原理

用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。 

当我们要进行替换时,新程序的数据和代码会将原来物理内存中的数据段和代码段进行替换。其实程序替换工作本质就是加载!!! 

替换函数

其实有六种以exec开头的函数,统称exec函数:

#include <unistd.h>`
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ...,char *const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);

int execve(const char *path, char *const argv[], char *const envp[]);

这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。
如果调用出错则返回-1
所以exec函数只有出错的返回值而没有成功的返回值。 

这些函数原型看起来很容易混,但只要掌握了规律就很好记。 

l(list) : 表示参数采用列表
v(vector) : 参数用数组
p(path) : 有p自动搜索环境变量PATH
e(env) : 表示自己维护环境变量

我们强调一下结尾带e的exec*的函数,其就是表示自己维护环境变量。我们想要子进程全部继承父进程的全部环境变量直接可以。如果单纯再父进程的环境变量中添加一些环境变量可以使用putenv函数。但是我们子进程如果想要自己拥有一个全新的环境变量,我们可以使用exec*函数中后面带e的,他会将父进程继承的环境变量覆盖掉!!! 

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>int main()
{char *const env[] ={(char*)"haha=hehe",(char*)"PATH=/",NULL};printf("I am a process, pid: %d\n", getpid());//putenv("MYVAL=bbbbbbbbbbbbbbbbbbbbbbbbbbbb");pid_t id = fork();if(id == 0){//extern char**environ;sleep(1);//execle("./mytest", "mytest", NULL, environ); // 我们传递环境变量表了吗??no. 子进程默认就拿到了.他是怎么做到的?execle("./mytest", "mytest", NULL, env); // 我们传递环境变量表了吗??no. 子进程默认就拿到了.他是怎么做到的?//execl("/usr/bin/python3", "python3", "test.py", NULL);//execl("/usr/bin/bash", "bash", "test.sh", NULL);//execl("./mytest", "mytest", NULL); // 我们传递环境变量表了吗??no. 子进程默认就拿到了.他是怎么做到的?//char *const argv[] = {//    (char*)"ls",//    (char*)"-a",//    (char*)"-l"//};//sleep(3);//printf("exec begin...\n");//execvp("ls", argv);//execv("/usr/bin/ls", argv);//execl("/usr/bin/ls", "ls", "-a", "-l", NULL); //NULL 不是 "NULL"//execlp("ls", "ls", "-a", "-l", NULL); //NULL 不是 "NULL"//execl("/usr/bin/top", "/usr/bin/top", NULL); //NULL 不是 "NULL"printf("exec end ...\n");exit(1);}pid_t rid = waitpid(id, NULL, 0);if(rid > 0){printf("wait success\n");}exit(1);
}

 

exec调用举例如下:

#include <unistd.h>
int main()
{
char *const argv[] = {"ps", "-ef", NULL};
char *const envp[] = {"PATH=/bin:/usr/bin", "TERM=console", NULL};
execl("/bin/ps", "ps", "-ef", NULL);
// 带p的,可以使用环境变量PATH,无需写全路径
execlp("ps", "ps", "-ef", NULL);
// 带e的,需要自己组装环境变量
execle("ps", "ps", "-ef", NULL, envp);
execv("/bin/ps", argv);
// 带p的,可以使用环境变量PATH,无需写全路径
execvp("ps", argv);
// 带e的,需要自己组装环境变量
execve("/bin/ps", argv, envp);
exit(0);
}

 事实上,只有execve是真正的系统调用,其它五个函数最终都调用 execve,所以execve在man手册 第2节,其它函数在man手册第3节。这些函数之间的关系如下图所示。

总结:

细节1:程序替换一旦成功,exec*后续代码不再执行,因为被替换掉了。

细节2:exec*只有失败有返回值,没有成功返回值。

细节3:替换完成,不再创建新的程序。

细节4:创建一个进程先创建PCB、地址空间、页表,再将程序加载到内存中。 

这些函数功能上没有任何区别,区别就在于传递的参数不同! 


以上就是全部内容,感谢大家观看!!!

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

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

相关文章

傲医医疗集成引擎 Rhapsody 在超融合信创平台表现如何?

作者&#xff1a;SmartX 商业团队 黄玉辉 随着越来越多的医疗用户基于超融合基础设施实现 IT 基础架构信创转型&#xff0c;超融合信创架构在医疗业务场景中的实际表现也得到更多关注。尤其是集成平台业务场景——作为三甲医院互联互通评级中不可缺少的核心业务系统&#xff0…

“云环境下的等保测评新趋势:混合云安全策略与合规性考量“

在云环境尤其是混合云场景下&#xff0c;等保测评&#xff08;信息安全等级保护测评&#xff09;的新趋势聚焦于更加灵活、高效且全面的安全策略&#xff0c;以及确保合规性。以下是几个关键点&#xff1a; 混合云安全策略的新趋势&#xff1a; 1. 统一安全管理&#xff1a;由…

概率分布、回归分析、假设检验……用 DolphinDB 函数库快速实现概率统计分析

在金融和物联网等领域&#xff0c;概率统计与分析扮演着至关重要的角色。DolphinDB 作为一款强大的时序数据库&#xff0c;提供了一系列内置的概率统计与分析函数&#xff0c;能够满足用户的各种需求。 金融领域 风险管理&#xff1a;通过概率统计分析&#xff0c;金融机构可…

【主题广泛|投稿优惠】2024年交通运输与信息科学国际会议(ICTIS 2024)

2024年交通运输与信息科学国际会议&#xff08;ICTIS 2024&#xff09; 2024 International Conference on Transportation and Information Science 【重要信息】 大会地点&#xff1a;青岛 大会官网&#xff1a;http://www.icictis.com 投稿邮箱&#xff1a;icictissub-conf.…

【Vue】成绩案例

文章目录 一、功能描述二、思路分析三、完整代码 一、功能描述 1.渲染功能 2.删除功能 3.添加功能 4.统计总分&#xff0c;求平均分 二、思路分析 渲染功能 v-for :key v-bind:动态绑定class的样式&#xff08;来回切换&#xff09; 删除功能 v-on绑定事件&#xff0c; 阻止…

线性代数|机器学习-P2 A的列向量空间

文章目录 1. Ax矩阵的形式2. ACR 矩阵分解2.1 rank1 矩阵分解2.2 rank2 矩阵分解2.3 ACMR,求M 3. Ax 向量 1. Ax矩阵的形式 假设我们有如下矩阵A&#xff1a; A x [ 2 1 3 3 1 4 5 7 12 ] [ x 1 x 2 x 3 ] \begin{equation} Ax\begin{bmatrix} 2&1&3\\\\ 3&1&am…

【微信小程序开发】小程序中的上滑加载更多,下拉刷新是如何实现的?

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

期望20K,2年golang深圳某互联网小公司一面

后续约了二面&#xff08;CTO面&#xff09;&#xff0c;需要到现场&#xff0c;基本没问啥具体的技术知识&#xff0c;都是聊规划和个人职业目标 一面 1、假设访问百度网站&#xff0c;从在浏览器输入网址&#xff0c;到最终页面展示出来&#xff0c;中间会发生哪些事情&…

使用Python和wxPython将PNG文件转换为JPEG文件

简介&#xff1a; 在图像处理中&#xff0c;有时候我们需要将PNG格式的图像文件转换为JPEG格式。本篇博客将介绍如何使用Python编程语言和wxPython图形用户界面库&#xff0c;以及Pillow图像处理库来实现这一转换过程。通过本文的指导&#xff0c;您将学习如何快速将指定文件夹…

30分钟快速上手scapy

在前两篇博客中&#xff0c;笔者简单分享了如何使用scapy进行网络流量的分析&#xff0c;但比较粗略&#xff0c;没有详细说明&#xff0c;本文我们就0开始&#xff0c;一起快速学习一下scapy 框架的使用。 另附之前的两篇博客&#xff0c;有需要快速参考代码的请自取&#xf…

外贸如何找国外客户?

外贸客户开发国外客户可以使用多种软件来完成&#xff0c;以下是一些常用的软件推荐&#xff1a; 即时通讯软件&#xff1a; SKYPE&#xff1a;一款功能丰富的即时通讯软件&#xff0c;支持视频聊天、多人语音会议、文件传输等&#xff0c;方便与海外客户进行实时沟通。Line&a…

用增之Firebase

目录 简介 开发准备&#xff1a; 1、在Firebase平台创建项目 2、将项目关联到应用 3、项目配置 简介 前面讲了google ddl部分&#xff0c;本篇为Firebase的事件上报部分&#xff0c;包括在FireBase平台创建应用 &#xff0c; 如果有用到ddl…

【网络安全的神秘世界】JavaScript

&#x1f31d;博客主页&#xff1a;泥菩萨 &#x1f496;专栏&#xff1a;Linux探索之旅 | 网络安全的神秘世界 | 专接本 01-JavaScript简介 JavaScript背景 2003年之前&#xff0c;JavaScript被称为“牛皮藓”&#xff0c;用来制作页面上的广告、弹窗、漂浮的广告 2004年&a…

小猪APP分发:让APP封装变得如此简单

你是否曾经在开发完一款APP后&#xff0c;为了封装、分发而头疼不已&#xff1f;别担心&#xff0c;小猪APP分发来拯救你了&#xff01;这款神器不仅能让你的工作变得更加高效&#xff0c;还能让你的APP在各大平台上顺利分发。 小猪APP封装www.ppzhu.net APP封装的挑战 开发一…

docker 停止重启容器命令start/stop/restart详解(容器生命周期管理教程-2)

Docker 提供了多个命令来管理容器的生命周期&#xff0c; 其中start、stop 和 restart。这些命令允许用户控制容器的运行状态。 1. docker start 命令格式&#xff1a; docker start [OPTIONS] CONTAINER [CONTAINER...]功能&#xff1a; 启动一个或多个已经停止的 Docker …

WLAN工作原理

目录 一、引言二、FAT AP的工作模式与挑战三、FIT APAC的组网方式四、CAPWAPI隧道技术五、WLAN网络的关键报文六、漫游机制七、总结 一、引言 随着无线网络的迅猛发展&#xff0c;WLAN&#xff08;无线局域网&#xff09;技术已广泛应用于各个领域&#xff0c;为企业和个人提供…

若依 ruoyi-vue element-ui el-cascader 级联选择器 选择任意一级选项,去掉单选按钮,选中点击后隐藏

Cascader 级联选择器 选择任意一级选项&#xff0c;去掉单选按钮。 这兄弟文章写的可以&#xff0c;查了一堆文章&#xff0c;基本搞完才发现。 官方的例子不支持选中后自动关闭&#xff0c;要点击旁边空白&#xff0c;并且单选框太小了。 <el-form-item label"所属地…

基于SSM+Jsp的二手手机回收平台系统

开发语言&#xff1a;Java框架&#xff1a;ssm技术&#xff1a;JSPJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包…

libsystemctlm-soc项目分析

概述 libsystemctlm-soc项目是Xilinx的SystemC库。 环境安装 verilator安装 # Prerequisites: #sudo apt-get install git help2man perl python3 make autoconf g flex bison ccache #sudo apt-get install libgoogle-perftools-dev numactl perl-doc #sudo apt-get insta…

JavaScript 从入门到精通Object(对象)

文章目录 对象文本和属性方括号计算属性 属性值简写属性名称限制属性存在性测试&#xff0c;“in” 操作符“for…in” 循环像对象一样排序 总结✅任务你好&#xff0c;对象检查空对象对象属性求和将数值属性值都乘以 2 对象引用和复制通过引用来比较克隆与合并&#xff0c;Obj…