0


进程控制详解

fork函数

进程退出

​​​​​​​进程等待

阻塞与非阻塞

程序替换

制作简易的shell

fork函数

写时拷贝

如下图,在子进程要修改内容时,os会让子进程发生缺页中断,然后为它开辟一块空间,然后把

父****进程的值拷贝过来

fork常规用法

fork常规用法 一个父进程希望复制自己,使父子进程同时执行不同的代码段

一个进程要执行一个不同的程序

fork调用失败的原因

系统中有太多的进程

实际用户的进程数超过了限制

另外,创建进程是有成本的(时间+空间)

进程退出

main函数的return值是进程的退出码,用来衡量结果对不对

代码运行完毕****结果正确

退出码为0,一般表示是success!

代码运行完毕****结果不正确

退出码为非0,则表示failed,因为结果不正确的原因有很多种,所有退出码也就有多种就比如考

试考得差你爸妈会问****你原因,考得好一般不会问

代码异常终止

如下图,代码异常终止后,程序就会崩溃,这时进程码也就没有意义了!

main函数return,代表进程退出,非main函数退出,代表函数返回

exit在任意地方调用,都代表终止进程,参数是退出码!

如下图,printf函数中没有\n,无法在printf语句结束后刷新缓冲区,数据是暂时被保存在输出缓冲

区中,exit或main中的return本身就会要求系统进行缓冲区刷新!所有才会打印出来

_exit终止进程,是强制终止进程,不会进行进程的后续收尾工作,比如刷新缓冲区(用户级缓冲区)

exit与_exit的区别,如下图

进程退出,在系统层面,少了一个进程,free PCB,free mm_struct,free 页表和各种映射关

系,****代码+数据申请的空间也要被free掉!

进程等待

父进程fork创建子进程,是为了让其帮助父进程完成某种任务,而父进程需要通过wait/wait等待子

进程退出

为什么要让父进程等待?

通过获取子进程退出的信息,能够得知子进程执行结果

可以保证:时序问题,子进程先退出,父进程后退出

进程退出的时候会先进入僵尸状态,会造成内存泄漏的问题,需要通过父进程wait,释放该子进程

占用的资源!

另外,进程一旦变成僵尸状态,那kill -9也无能为力,因为谁也没有办法杀死一个已经死去的进程

**wait/waitpid **

注意:僵尸:PCB保存进程退出时的退出数据!

下面的stat_loc是一个输出型参数,来反映是进程退出的三种结果中的哪一个

如下图,子进程的僵尸状态被解除且退出了!** 而且也保证了子进程先退出,父进程后退出**

等待指定一个进程,如下图中的id就是子进程的编号

-1表示等待任意一个子进程,等价于wait!

也有可能会等待失败,比如随便传一个id

如下图,父进程拿到什么status结果,一定和子进程如何退出强相关!!!这个结果也就是前面所

提到的进程退出的三种结果之一,如果是收到的退出码,则表示代码运行完毕,而如果是收到的某

种信号,则表示代码异常终止,因为其本质是这个进程因为异常问题,导致自己收到了某种信号!

为了反应从子进程收到的信息,本来有32个bit位,就只用16个,高位上的8个bit来表示退出码,

地位上的7个表示终止信号,而中间的一个

最终一定要让父进程通过status得到子进程的结果!

如下图,测试的是代码运行完毕不正确结果的情况

代码异常终止的情况

除零异常

进程被干掉了

如下图,也可以用宏的方式来得到退出码

bash是命令行启动的所有进程的父进程!而bash一定是通过wait方式得到子进程的退出结果,所

以****我们能通过echo$?查到子进程的退出码!

阻塞与非阻塞

**确定是阻塞还是非阻塞由wait中的第三个参数options确定 **

阻塞:比如你要和你的某个同学去图书馆复习,你要他给你画重点,你已经到了他家楼下,但

是他要你等他30分钟,你就一直等,而且什么都不干,就一直注视着他家楼上。options为0

非阻塞:你等他的过程中,时不时的给他打电话询问他还有多久。options为WNOHANG,可能需

要多次检测:基于非阻塞等待的轮询方案

阻塞的本质:其实是进程的PCB被放入了等待队列,并将进程的状态改为S状态

返回的本质:进程的PCB从等待队列拿到R队列,从而被CPU调度

非阻塞分为几种情况

子进程根本没有退出

子进程退出,waitpid(调用成功或者失败)

ret==0时,子进程没有退出,但是waitpid等待是成功的,需要父进程重复进行等待

ret>0时,子进程退出了,waitpid也成功了,获得了对应的结果

ret<0时,等待失败

程序替换

目前我们创建子进程的目的:if else让子进程执行父进程的代码的一部分!

为什么需要程序替换?

如果想让子进程执行****一个"全新的程序",就需要进行程序替换

概念

进程不变,仅仅替换当前进程的代码和数据的技术,叫做进程的程序替换!没有创建新的进程!

程序本质就是一个文件!

文件=程序代码+程序数据

进程的程序替换使用

如下图,将本来要执行printf("哈哈")替换成了执行ls命令

程序替换的本质就是把程序的进程代码+数据,加载到特定进程的上下文中!

C/C++程序要运行,必须得通过加载器先加载到内存中!加载器也就是exec*程序替换函数!

如下图,子进程进行程序替换,但父进程没有被影响,是因为进程具有独立性,虽然父子进程代码

是共享的,但是进程程序替换会更改代码区的代码,也要发生写时拷贝

如下图,以及前面所述可知,只要进程的程序替换成功,就不会执行后续代码,意味着exec*函

数,成功的时候,不需要返回值检测。只要exec*返回了,就一定时因为调用失败了!!!

各个程序替换函数的基本使用

命名理解

l(list) : 表示参数采用列表

v(vector) : 参数用数组

p(path) : 有p自动搜索环境变量PATH

**e(env) : 表示自己维护环境变量 **

execl

execv

execlp

execvp

execle

execve:系统调用

上述所有的接口,看起来是没有太大区别的,只有一个就是参数的不同!而有这么多的接口,是为

了满足不同的应用场景

如下图,可以同时创建多个可执行程序

制作简易的shell

打印命令提示符

这里没用\n,是因为不符合预期,因为输入命令时是和命令提示符在同一行的,因为没有\n,所以需要fflush来****刷新缓冲区

获取命令字符串

定义一个宏,便于修改字符串大小,用fgets来获取字符串,而不用scanf则是因为输出的命令可能

会包含空格,如ls -a -l,同时还需要将回车去掉,如absds\n\0,要将\n置为\0

将字符串进行分隔

首先在循环最开始创建一个指针数组,将字符串按空格分隔开,然后统计在一个数组中,便于程序替换时使用

执行第三方命令

获取子进程返回的退出码

如下图,返回上层命令输入后,并没有得到想要的结果,所以还需要检测命令是否是需要shell本

身执行的,内建命令


本文转载自: https://blog.csdn.net/weixin_58867976/article/details/126714809
版权归原作者 风影66666 所有, 如有侵权,请联系我们删除。

“进程控制详解”的评论:

还没有评论