学习系统编程No.7【进程替换】
引言:
北京时间:2023/3/21/7:17,这篇博客本来昨天晚上就能开始写的,但是由于笔试强训的原因,导致时间用在了做题上,通过快2个小时的垂死挣扎,我充分意识到了自己做题能力的缺陷和运用新知识的缺陷,所以我需要把重心给转移一下了,以后做题才是我的头号目标,虽然我在很久以前可能就说过这样的话,但是这次我是认真的,就算我做题不行,但是我看代码还是行的,所以我每天必看一些做题代码,来加深自己对知识的运用,希望不久之后,能有所进步吧!哪怕只是一丝丝!所以今天我们就来学习上篇博客剩下的有关进程控制的内容,和有关进程程序替换的知识吧!
复习进程等待
昨天已经浅浅的了解了什么是进程等待,进程等待的目的就是为了终止僵尸进程,回收僵尸进程所占的资源,并且获得进程的退出码和退出信号,并且我们了解了进行进程等待的两个接口函数wait/waitpid,通过这两个接口,我们就可以去调用操作系统提供给我们的系统调用,这样就可以利用系统调用的形式来完成进程等待了;我们昨天已经知道使用wait接口就是用来等待一个进程的子进程,并且有代码演示,这里不多做理解,昨天我们也了解了waitpid这个接口,发现该接口有三个参数,并且其中第一个参数pid
,它的作用和wait的参数差不多,本质就是为了等待一个进程,pid=0等待的是该进程的子进程,pid>0,等待的则是任意一个pid相等的进程,和第二个参数status
,我们了解到,该参数是一个位图结构的参数,不单单是整形类型,本质为位图结构就是为了可以同时从一个参数上获取退出码和退出信号两个码值,第三个参数options
,就是用于判断子进程是否正常终止,父进程是否继续等待的问题,所以接下来,我们就承接这上篇博客,把剩下的有关位图结构,也就是有关status参数的知识再深入学习一下,如下:
继续谈status
上篇博客,我们了解到status是一个位图结构,可以同时返回退出码和退出信号,并且退出码是由32个比特位中的后16个比特位中的次第8位构成的,退出信号是由最后7个比特位构成的(core down先不谈),所以按照这个逻辑,此时我们想要拿到,status中的退出信号和退出码是有办法的,没错,就是使用我们在C语言中所学的位运算的概念,具体这里不多做讲解(具体就是左移和右移),左移以为表示的就是该值乘2,右移一位表示的就是除2,并且还涉及到了按位或、按位与、按位异或的知识,如下图:
通过上述的 (status >> 8)& 0xFF、status & 0x7F
此时我们就可以获得该进程的退出码和退出信号了,但是要知道,上述获取子进程退出码和退出信号的方式,是我们自己通过位图结构的概念,通过位运算获得的,但是注意,在Linux操作系统内部,它本身是为我们提供了获取子进程退出码和退出信号的方式,如下图:
所以按照我们上述所说,父进程就可以通过使用waitpid接口,来调用系统调用,从而获取子进程的退出码和退出信号,但是具体在调用系统调用接口的时候,本质上这些工作还是由我们的操作系统来完成了,因为只有操作系统才有获取进程退出码和退出信号的资格,所以此时就有一个问题,就是父进程具体是如何通过操作系统来获取子进程的退出码和退出信号呢?
首先明白,无论是父进程还是子进程,它们都是一个独立的进程,所以每个进程都拥有自己的地址空间,进程pcb、页表和物理内存等资源;并且明白,子进程pcb内不仅有从父进程pcb中继承的相关属性,而且还有两个和子进程退出码和退出信号至关重要的属性 (int exit_code ,int exit_signal)
,从该属性的名字就可以看出,这两个子进程pcb属性代表的就是进程退出码和退出信号,表示的仅仅就是两个整数而已;
明白了这些之后,此时就可以回答该问题了,如下:
当子进程执行完毕之时,操作系统会将main函数对应的返回值写到该进程pcb的int exit_code当中,如果该进程异常,则将对应的退出信号写到该进程pcb的int exit_signal当中,并且最后在进程退出之后,操作系统会将该进程的pcb维护起来,所以当该子进程pcb被操作系统维护起来之后,此时父进程就可以通过调用系统接口 wait/waitpid
通过操作系统来去找到该退出进程对应的进程pcb,可以找到子进程pcb的具体原理是因为,父进程在调用waitpid接口的时候,已经把子进程的pid告诉了父进程(也就是传参的时候已经把进程子pid给给了waitpid接口),所以最后通过子进程pid,进而找到该子进程的pcb,进而找到pcb中的该子进程的退出码 int exit_code
和退出信号 int exit_signal
,最后再利用waitpid中的第二个参数status(输出型参数)将退出码和退出信号返回给父进程(当然也就是上述的ret_id
变量)
总:父进程获取子进程的退出码和退出信号的本质就是通过操作系统读取子进程的内核数据结构(pcb)
并且有的同学非常的好奇,就是有没有一种可能,就是父进程在等待的时候,子进程一直没有退出呢?或者说父进程在等待的时候具体是在干什么呢?
所以此时我们就借着上述问题来谈一谈,什么是阻塞等待和非阻塞轮询等待
首先我们要明白,当子进程没有退出的时候,父进程在干什么呢?
谈到这个,此时又不得不谈谈waitpid的第三个参数,options,上篇博客我们说了,这个参数的作用就是用于判断子进程是否正常终止,父进程是否继续等待问题,并且该参数我们默认给给的是一个叫 WNOHANG
的参数,具体意思:就是用于判断该子进程是否终止,如果该子进程未终止,那么此时waitpid()函数就返回0,如果该子进程终止,则返回该子进程的pid,此时就可以很好的通过这第三个参数来判断一个进程的子进程是否处于终止状态,所以明白了这点,此时就可以明白父进程是可以通过该参数来控制自己的行为,也就是上述所说的阻塞等待和非阻塞轮询等待!
谈谈父进程的阻塞等待
从上述的知识,我们可以知道,如果waitpid()函数返回0,表示的就是该子进程没有结束,子进程没有结束,那么父进程此时可以干什么呢?首先第一种情况,父进程一直在调用waitpid进行等待 ,此时的一直进行等待,本质上表示的就是阻塞等待,也就是表示父进程此时从本来的正常运行状态变成了一个阻塞状态,变成了一个等待子进程的状态;所以我们可以明白,当父进程处于阻塞状态之时,父进程是不在CPU的运行队列之中,而是在某一个阻塞队列之中;并且此时再深入理解,还可以明白,父进程处于阻塞等待之时,子进程处于运行状态,那么等子进程执行完之后呢?父进程如何从阻塞状态重新变成运行状态来为子进程收尸呢? 谈到这个问题,此时就又涉及到了,子进程的进程pcb,在子进程pcb内部,我们可以理解存在一个指向父进程的指针,所以当子进程一但退出(注意此时父进程肯定是不在运行队列,而是在阻塞队列的),此时子进程就可以利用pcb中指向父进程的指针,重新找到父进程(唤醒),此时父进程检测到之后(被唤醒),就又会从阻塞状态变成运行状态,然后被操作系统又重新链接到运行队列之中,最后父进程继续执行 wait/waitpid
,最终就将子进程给回收
所以上述就是父进程阻塞等待全过程
谈谈父进程的非阻塞轮询等待
搞懂了上述父进程的阻塞等待,此时就可以明白父进程肯定还有一种非阻塞等待(毕竟我是父进程,你是小子,我才是老子,我凭什么一定要等你呢?)所以,此时还是同理,当我们通过waitpid()函数的返回值,判断出该子进程还没有结束时,我们就可以让父进程不需要一直进行等待(不需要一直处于阻塞状态),具体代码如下:
所以此时通过上述的代码,我们就可以很好的理解什么是父进程的阻塞等待和非阻塞轮询等待了,并且可以将其很好的抽象成一个生活中场景,如下:
场景一:
周末出去玩,李四打电话给张三,问张三去不去,此时张三说去,但是要准备一下(子进程未终止),李四说好,我等你(父进程等待),但是电话别挂(阻塞等待)
场景二:
如法炮制,还是周末出去玩,李四打电话给张三,问张三去不去,张三还是去,并且也还是需要准备一下,此时李四还是说好,但是此时李四却把电话给挂断了,在电话挂断之后,李四想了想也去准备了一下,过了一会之后,李四准备的差不多了,就又打电话给张三,张三却还是没有准备好,此时李四就又把电话挂了,又去准备东西了,同理,李四一直打电话问张三好了没有(非阻塞轮询等待),而张三却一直没好,直到最后好了(子进程终止)
所以上述的两种情况(李四给张三打电话,表示的就是父进程使用waitpid()等待子进程,)第一种情况电话不挂表示的就是父进程处于阻塞队列,这种情况也就是阻塞调用;第二种情况,表示我打完电话,问完情况,我们把电话挂掉,此时表示的就是父进程不处于阻塞等待,也就是表示该调用,此时是属于非阻塞调用,(非阻塞调用的好处,就是父进程可以干自己想干的事情,例:准备自己的东西)
所以上述的两种场景就是父进程阻塞等待和非阻塞等待的两个现实生活抽象场景,可以非常好的凸显出父进程等待的两种情况
总:父进程可以通过参数进行判断,然后来控制自己的行为,也就是上述所说的阻塞等待和非阻塞轮询等待!
总结:进程控制的三个话题:进程创建、进程等待、进程终止此时就算告一段落啦!现在让我们呢一起开始新知识的学习吧!
进程替换
搞定了上述的知识,此时我们就进入今天的正菜,什么是进程替换,在弄懂什么是进程替换之前,我们引入一个问题,就是为什么要创建子进程?所以我们要明白,我们创建子进程的目的:就是为了让子进程帮我执行某些特定的任务,但是此时让子进程去执行某些代码的时候,就会出现如下两种情况:
1. 让子进程执行父进程的部分代码,也就是一个可执行文件中的部分代码(这些代码本质都是属于父进程)
2. 子进程想要执行一个全新的属于自己的代码(本质就是不想执行父进程的代码)
所以碰到上述第二种情况,此时就可以引出我们的新概念:进程程序替换
如下图:就是一个最简单的进程程序替换demo
所以使用execl接口的本质就是进行进程的切换,让该可执行文件形成的进程去执行Linux操作系统中bin目录下的ls指令(ls可执行文件),也就是表明,当我们使用了进程替换函数时,我们可以在任意一个可执行文件中执行任意的指令,本质是进程替换
总:进程程序替换,就是让一个进程去执行另一个在磁盘(Linux操作系统下的bin目录)当中的程序(可执行文件),把一个新的程序运行起来
程序替换的原理
首先明白,创建一个进程,该进程肯定是有对应的pcb、页表、地址空间和物理内存,所以我们就从这些进程的必要条件入手,来看看进程替换到底是什么,如下图:
从图中,我们可以了解到以下知识:
当前进程的代码等数据是需要通过页表映射到物理内存的特定区域,所以当一个进程执行了部分代码后,当其执行到了系统调用接口(execl等……),进程此时就会根据我所传入的程序的路径(Linux操作系统下的bin
目录)和我要执行的程序的名称和选项(“ls” “a”
),然后把磁盘当中(通过对应的路径找到bin)的其它的程序的代码和数据加载到内存,最后用新程序的代码来替换我们原程序中的代码,用新程序的数据来替换原程序中的数据(这个过程不是在地址空间完成的,是在物理内存中完成的);所以总的来说: 程序在替换时,就相当于原进程的内核数据结构不变,把原进程在物理内存中的代码和数据替换为新进程的代码和数据(从磁盘中加载),此时这个行为的发生就叫做程序替换。
并且要明白,进程在进行程序替换的时候,是没有创建新的进程的,本质还是程序替换的本质。
并且此时明白,可以进行进程程序替换的接口一共有7个,此时我们已学其一,剩下的6个都是同理,只是在使用上不同而已,所以我们今天浅浅的摸了一下程序替换就行了(明天要晨跑),今天博客就这样吧!撤了!
补充小知识点:
fork函数的两个返回值浅显理解:一个是父进程返回子进程的pid,一个是子进程没有子进程所以返回0,感兴趣的同学,可以参看下面这个链接;为什么fork有两个返回值
北京时间:2023/3/21/22:45
总结:像程序替换(execl)这样的系统接口,等我们学到了文件操作和网络的时候,我们就经常会谈到,并且会用到更多,所以革命还未胜利,挑战任在继续,加油吧,少年!
相关文章:

学习系统编程No.7【进程替换】
引言: 北京时间:2023/3/21/7:17,这篇博客本来昨天晚上就能开始写的,但是由于笔试强训的原因,导致时间用在了做题上,通过快2个小时的垂死挣扎,我充分意识到了自己做题能力的缺陷和运用新知识的缺…...
【3.22】操作系统内存管理(整理)、Java并发
3. 内存管理 为什么要有虚拟内存? 我们想要同时在内存中运行多个程序,就需要把进程所使用的地址隔离,所以使用了虚拟内存。简单来说,虚拟内存地址是程序使用的内存地址。物理内存地址是实际存在硬件里面的地址。 操作系统为每个…...
电脑文件丢失怎么找回来
电脑文件丢失怎么找回来?最近打开电脑时,它启动得很慢。刚刚开始我没有没在意,就重启了当我再次打开电脑时,发现桌面上的文件消失了,面对这种意外情况,有什么办法可以快速找到呢? 电脑文件丢失后,想要找回…...

Python(白银时代)——面向对象
基本概念 面向过程 是早期的一个编程概念,类似函数,但是没有返回值 具体做法: 把完成某个需求的所有步骤,从头到尾 逐步实现 将某些功能独立的代码 封装成一个又一个 函数 然后顺序调用不同的函数 特点: 注重 步骤…...

Python流星雨代码
前言 用Python画场流星雨看看,源码见文末公众号哈。 流星类 def __init__(self): self.r ra.randint(50,100) self.t ra.randint(1,3) self.x ra.randint(-2000,1000) #流星的横坐标 self.y ra.randint(0,500) #流星…...

Java语言-----类与对象的秘密
目录 前言 一、类与对象的介绍 二、类的实例化 三.类与对象的使用方法 3.1对象的初始化 3.2内存显示图 四.this的使用方法 总结 😽个人主页: tq02的博客_CSDN博客-C语言,Java领域博主 🌈理想目标:努力学习,向Java进…...

大数据处理学习笔记2.1 初识Spark
文章目录零、本节学习目标一、Spark的概述(一)Spark的组件1、Spark Core2、Spark SQL3、Spark Streaming4、MLlib5、Graph X6、独立调度器、Yarn、Mesos(二)Spark的发展史1、发展简史2、目前最新版本二、Spark的特点(一…...

太强了,英伟达面对ChatGPT还有这一招...
大家好,我是 Jack。 今年可谓是 AI 元年,ChatGPT、AIGC、VITS 都火了一波。 我也先后发布了这几期视频: 这是一个大模型的时代,AI 能在文本、图像、音频等领域大放异彩,得益于大模型。而想要预训练大模型,…...

【微服务】—— Nacos注册中心
文章目录一、Nacos 注册中心的设计原理1、数据模型2、数据⼀致性3、负载均衡4、健康检查二、Nacos 注册中心服务数据模型1、服务(Service)和服务实例(Instance)1)定义服务2)服务元数据3)定义实例…...

GPT-4是个编程高手,真服了!
上周给大家发了一个GPT-4教数学的介绍,很多人都被震撼了,感觉有可能在教育行业引发革命。它在编程领域表现如何?先不说能否替代程序员,这个还有待更多的测试和反馈,我想先试试它能不能像教数学那样教编程。我找了个Jav…...

基于深度学习的车型识别系统(Python+清新界面+数据集)
摘要:基于深度学习的车型识别系统用于识别不同类型的车辆,应用YOLO V5算法根据不同尺寸大小区分和检测车辆,并统计各类型数量以辅助智能交通管理。本文详细介绍车型识别系统,在介绍算法原理的同时,给出Python的实现代码…...

【蓝桥杯C++】3月21日刷题集训ABC-附百分代码,一目了然
目录 刷题集训 A Day 1 成绩分析 Day 1 饮料换购 刷题集训 B Day 1 分巧克力 Day 1 递增三元组 Day 1 小明的衣服 刷题集训 C Day 1 数字三角形 Day 1 跳跃 Day 1 蓝太子序列 刷题集训 A Day 1 成绩分析 题目描述 小蓝给学生…...

HBase高手之路4-Shell操作
文章目录HBase高手之路3—HBase的shell操作一、hbase的shell命令汇总二、需求三、表的操作1.进入shell命令行2.创建表3.查看表的定义4.列出所有的表5.删除表1)禁用表2)启用表3)删除表四、数据的操作1.添加数…...
聊聊SQL审计功能
什么是sql审计SQL审计是指对SQL语句的执行情况进行记录和追踪,包括SQL语句的执行时间、执行次数、执行结果等信息。通过SQL审计,可以对数据库的使用情况进行监控和管理,包括对SQL注入、非法访问、数据泄露等安全问题的检测和防范,…...
Markdown常用语法(字体颜色)
一些不错的帖子 写CSDN博客时,调节字体大小、颜色及其他样式的常用操作方法 设置字体颜色 使用<font>标记: 这是红色字体:<font colorred>我是红色的字体</font>显示效果如下: 这是红色字体:我是…...

I2C模块理解
I2C模块理解 文章目录I2C模块理解1.配置I2C2.信号3.数据传输3.1主机发送3.2主机接收3.3从机发送3.4从机接收4.中断传输5.Aardvark1.配置I2C I2C的特征 只需要两条公共总线(线)即可控制I2C网络上的任何设备无需像UART通信那样事先约定数据传输速率。因此…...

手把手教你使用--常用模块--HC05蓝牙模块,无线蓝牙串口透传模块,(实例:手机蓝牙控制STM32单片机点亮LED灯)
最近在学STM32,基本的学完了,想学几个模块来巩固一下知识,就想到了蓝牙模块。玩啥好难过有很多博客教怎么连的,但自己看起来还是有点糊涂。模块的原理和知识点我就不讲解了,这里我主要手把手记录一下我是如何对蓝牙模块…...
MyBatis高频面试题
目录 1、Mybatis中#和$的区别 2、Mybatis的编程步骤是什么样的 3...

Redis基础篇
redis的三大特点: 支持多数据类型,支持持久化,单线程 多路IO复用 对键操作的命令: keys * 查看当前库所有key exists key 判断key是否存在 del key 删除 unlink key 非阻塞删除,异步删除 expire key …...
unity的C#学习——静态常量和动态常量的定义与使用
定义常量 在C#中,常量是一种不可改变的量,一旦被定义,其值就不能被修改。C#中有两种类型的常量,静态常量和动态常量。 1、静态常量的定义 静态常量是在编译时就已经确定其值的常量,使用const关键字定义。由于在编译…...

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造,完美适配AGV和无人叉车。同时,集成以太网与语音合成技术,为各类高级系统(如MES、调度系统、库位管理、立库等)提供高效便捷的语音交互体验。 L…...

深入剖析AI大模型:大模型时代的 Prompt 工程全解析
今天聊的内容,我认为是AI开发里面非常重要的内容。它在AI开发里无处不在,当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗",或者让翻译模型 "将这段合同翻译成商务日语" 时,输入的这句话就是 Prompt。…...
k8s从入门到放弃之Ingress七层负载
k8s从入门到放弃之Ingress七层负载 在Kubernetes(简称K8s)中,Ingress是一个API对象,它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress,你可…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序
一、开发环境准备 工具安装: 下载安装DevEco Studio 4.0(支持HarmonyOS 5)配置HarmonyOS SDK 5.0确保Node.js版本≥14 项目初始化: ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...

零基础设计模式——行为型模式 - 责任链模式
第四部分:行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习!行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想:使多个对象都有机会处…...
laravel8+vue3.0+element-plus搭建方法
创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...

Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战
说明:这是一个机器学习实战项目(附带数据代码文档),如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下,风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…...

【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)
本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...

Rust 开发环境搭建
环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行: rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu 2、Hello World fn main() { println…...
在 Spring Boot 项目里,MYSQL中json类型字段使用
前言: 因为程序特殊需求导致,需要mysql数据库存储json类型数据,因此记录一下使用流程 1.java实体中新增字段 private List<User> users 2.增加mybatis-plus注解 TableField(typeHandler FastjsonTypeHandler.class) private Lis…...