学习系统编程No.7【进程替换】

简介: 学习系统编程No.7【进程替换】

引言:

北京时间:2023/3/21/7:17,这篇博客本来昨天晚上就能开始写的,但是由于笔试强训的原因,导致时间用在了做题上,通过快2个小时的垂死挣扎,我充分意识到了自己做题能力的缺陷和运用新知识的缺陷,所以我需要把重心给转移一下了,以后做题才是我的头号目标,虽然我在很久以前可能就说过这样的话,但是这次我是认真的,就算我做题不行,但是我看代码还是行的,所以我每天必看一些做题代码,来加深自己对知识的运用,希望不久之后,能有所进步吧!哪怕只是一丝丝!所以今天我们就来学习上篇博客剩下的有关进程控制的内容,和有关进程程序替换的知识吧!


image.png


复习进程等待

昨天已经浅浅的了解了什么是进程等待,进程等待的目的就是为了终止僵尸进程,回收僵尸进程所占的资源,并且获得进程的退出码和退出信号,并且我们了解了进行进程等待的两个接口函数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,并且还涉及到了按位或、按位与、按位异或的知识,如下图:


64.png

通过上述的 (status >> 8)& 0xFF、status & 0x7F 此时我们就可以获得该进程的退出码和退出信号了,但是要知道,上述获取子进程退出码和退出信号的方式,是我们自己通过位图结构的概念,通过位运算获得的,但是注意,在Linux操作系统内部,它本身是为我们提供了获取子进程退出码和退出信号的方式,如下图:


65.png


所以按照我们上述所说,父进程就可以通过使用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()函数的返回值,判断出该子进程还没有结束时,我们就可以让父进程不需要一直进行等待(不需要一直处于阻塞状态),具体代码如下:


66.png


所以此时通过上述的代码,我们就可以很好的理解什么是父进程的阻塞等待和非阻塞轮询等待了,并且可以将其很好的抽象成一个生活中场景,如下:


场景一:

周末出去玩,李四打电话给张三,问张三去不去,此时张三说去,但是要准备一下(子进程未终止),李四说好,我等你(父进程等待),但是电话别挂(阻塞等待)

场景二:

如法炮制,还是周末出去玩,李四打电话给张三,问张三去不去,张三还是去,并且也还是需要准备一下,此时李四还是说好,但是此时李四却把电话给挂断了,在电话挂断之后,李四想了想也去准备了一下,过了一会之后,李四准备的差不多了,就又打电话给张三,张三却还是没有准备好,此时李四就又把电话挂了,又去准备东西了,同理,李四一直打电话问张三好了没有(非阻塞轮询等待),而张三却一直没好,直到最后好了(子进程终止)


所以上述的两种情况(李四给张三打电话,表示的就是父进程使用waitpid()等待子进程,)第一种情况电话不挂表示的就是父进程处于阻塞队列,这种情况也就是阻塞调用;第二种情况,表示我打完电话,问完情况,我们把电话挂掉,此时表示的就是父进程不处于阻塞等待,也就是表示该调用,此时是属于非阻塞调用,(非阻塞调用的好处,就是父进程可以干自己想干的事情,例:准备自己的东西)


所以上述的两种场景就是父进程阻塞等待和非阻塞等待的两个现实生活抽象场景,可以非常好的凸显出父进程等待的两种情况


总:父进程可以通过参数进行判断,然后来控制自己的行为,也就是上述所说的阻塞等待和非阻塞轮询等待!

总结:进程控制的三个话题:进程创建、进程等待、进程终止此时就算告一段落啦!现在让我们呢一起开始新知识的学习吧!


程序替换的原理

首先明白,创建一个进程,该进程肯定是有对应的pcb、页表、地址空间和物理内存,所以我们就从这些进程的必要条件入手,来看看进程替换到底是什么,如下图:

70.png


从图中,我们可以了解到以下知识:

当前进程的代码等数据是需要通过页表映射到物理内存的特定区域,所以当一个进程执行了部分代码后,当其执行到了系统调用接口(execl等……),进程此时就会根据我所传入的程序的路径(Linux操作系统下的bin目录)和我要执行的程序的名称和选项(“ls” “a”),然后把磁盘当中(通过对应的路径找到bin)的其它的程序的代码和数据加载到内存,最后用新程序的代码来替换我们原程序中的代码,用新程序的数据来替换原程序中的数据(这个过程不是在地址空间完成的,是在物理内存中完成的);所以总的来说: 程序在替换时,就相当于原进程的内核数据结构不变,把原进程在物理内存中的代码和数据替换为新进程的代码和数据(从磁盘中加载),此时这个行为的发生就叫做程序替换。


并且要明白,进程在进行程序替换的时候,是没有创建新的进程的,本质还是程序替换的本质。


并且此时明白,可以进行进程程序替换的接口一共有7个,此时我们已学其一,剩下的6个都是同理,只是在使用上不同而已,所以我们今天浅浅的摸了一下程序替换就行了(明天要晨跑),今天博客就这样吧!撤了!


补充小知识点:


fork函数的两个返回值浅显理解:一个是父进程返回子进程的pid,一个是子进程没有子进程所以返回0,感兴趣的同学,可以参看下面这个链接;为什么fork有两个返回值


北京时间:2023/3/21/22:45


71.png


image.png

总结:像程序替换(execl)这样的系统接口,等我们学到了文件操作和网络的时候,我们就经常会谈到,并且会用到更多,所以革命还未胜利,挑战任在继续,加油吧,少年!

相关文章
|
24天前
麒麟系统mate-indicators进程占用内存过高问题解决
【10月更文挑战第7天】麒麟系统mate-indicators进程占用内存过高问题解决
110 2
|
2月前
|
监控
MASM32写的免费软件“ProcView/系统进程监控” V1.4.4003 说明和下载
MASM32写的免费软件“ProcView/系统进程监控” V1.4.4003 说明和下载
|
26天前
麒麟系统mate-indicators进程占用内存过高问题解决
【10月更文挑战第5天】麒麟系统mate-indicators进程占用内存过高问题解决
99 0
|
2月前
|
监控 Ubuntu API
Python脚本监控Ubuntu系统进程内存的实现方式
通过这种方法,我们可以很容易地监控Ubuntu系统中进程的内存使用情况,对于性能分析和资源管理具有很大的帮助。这只是 `psutil`库功能的冰山一角,`psutil`还能够提供更多关于系统和进程的详细信息,强烈推荐进一步探索这个强大的库。
40 1
|
2月前
|
安全 开发者 Python
揭秘Python IPC:进程间的秘密对话,让你的系统编程更上一层楼
【9月更文挑战第8天】在系统编程中,进程间通信(IPC)是实现多进程协作的关键技术。IPC机制如管道、队列、共享内存和套接字,使进程能在独立内存空间中共享信息,提升系统并发性和灵活性。Python提供了丰富的IPC工具,如`multiprocessing.Pipe()`和`multiprocessing.Queue()`,简化了进程间通信的实现。本文将从理论到实践,详细介绍各种IPC机制的特点和应用场景,帮助开发者构建高效、可靠的多进程应用。掌握Python IPC,让系统编程更加得心应手。
31 4
|
2月前
|
缓存 Linux C语言
C语言 多进程编程(六)共享内存
本文介绍了Linux系统下的多进程通信机制——共享内存的使用方法。首先详细讲解了如何通过`shmget()`函数创建共享内存,并提供了示例代码。接着介绍了如何利用`shmctl()`函数删除共享内存。随后,文章解释了共享内存映射的概念及其实现方法,包括使用`shmat()`函数进行映射以及使用`shmdt()`函数解除映射,并给出了相应的示例代码。最后,展示了如何在共享内存中读写数据的具体操作流程。
|
2月前
|
监控 API
【原创】用Delphi编写系统进程监控程序
【原创】用Delphi编写系统进程监控程序
|
2月前
|
Linux C语言
C语言 多进程编程(七)信号量
本文档详细介绍了进程间通信中的信号量机制。首先解释了资源竞争、临界资源和临界区的概念,并重点阐述了信号量如何解决这些问题。信号量作为一种协调共享资源访问的机制,包括互斥和同步两方面。文档还详细描述了无名信号量的初始化、等待、释放及销毁等操作,并提供了相应的 C 语言示例代码。此外,还介绍了如何创建信号量集合、初始化信号量以及信号量的操作方法。最后,通过实际示例展示了信号量在进程互斥和同步中的应用,包括如何使用信号量避免资源竞争,并实现了父子进程间的同步输出。附带的 `sem.h` 和 `sem.c` 文件提供了信号量操作的具体实现。
|
4月前
|
运维 关系型数据库 MySQL
掌握taskset:优化你的Linux进程,提升系统性能
在多核处理器成为现代计算标准的今天,运维人员和性能调优人员面临着如何有效利用这些处理能力的挑战。优化进程运行的位置不仅可以提高性能,还能更好地管理和分配系统资源。 其中,taskset命令是一个强大的工具,它允许管理员将进程绑定到特定的CPU核心,减少上下文切换的开销,从而提升整体效率。
掌握taskset:优化你的Linux进程,提升系统性能
|
4月前
|
弹性计算 Linux 区块链
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
144 4
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)