进程等待与程序替换

进程控制

fork函数

我们创建多个进程以后,哪个进程先运行是不确定的,是由进程调度器决定。

整个的调度过程我们不可知,父子兄弟进程谁先运行我们不确定。

进程终止

exit()

为什么main函数总是返回0,这个东西给谁了,不能返回1,2,3等等其他的吗。

main函数的返回值表示的是进程运行完成时是否是正确的结果,如果不是,可以用不同的数字,表示不同的出错原因。

进程的退出场景:
代码运行完毕,结果正确
代码运行完毕,结果不正确
代码异常终止

0表示进程的退出码,进程的结果是正确的。

谁会关心我们进程运行完的结果呢?

那就是父进程,父进程要获取子进程不正确的原因。

结果不正确:是进程的退出码不为0。

main函数的返回值表示:运行完成是是否是正确的结果,如果不是,可以用不同的数字,表示不同的出错原因。

echo &? 打印最近一次运行结果的退出码。

strerror()把错误码转换为退出信息,描述错误码信息。

errno:c语言给我们提供的全局变量,保存的是最近一次的退出码。库函数调用出错以后,errno将会保存,errno保存的是最近一次的。

进程异常终止, 本质就可能是代码没有跑完,进程的退出码没有意义,我们就不关心退出码了。根本就不会执行最后一跳return语句。

进程出现了异常本质是我们的进程收到了对应的信号。

在这里插入图片描述

exit就是进程的退出码,return也可以达到向对应的效果。

exit函数是c语言的库函数。

exit在任意地方被调用都表示调用该进程直接退出

return只表示当前函数返回 ,在main函数中表示进程退出。

_exit系统调用

也能够提前终止,直接终止掉,缓冲区的数据就没有了。

exit是库函数是对_exit函数进行了包装。

我们printf函数一定是先把数据写入缓冲区,合适的时候,在进行刷新。

进程等待

是什么:

通过系统调用wait/waitpid,来进行对子进程状态检测与回收功能。

为什么:

进程等待的必要性,子进程如果退出了,

僵尸进程是无法被杀死的,需要通过进程等待来杀掉它,进而解决内存泄露的问题。(必须解决的)

我们要通过进程等待,获取子进程退出的情况,知道我布置给子进程的任务,它完成的怎么样了(可选择的)

怎么办:

代码:

在这里插入图片描述

在这里插入图片描述

如果有多个子进程,wait等待哪一个呢?

wait等待任意一个子进程退出。

如果子进程一直不退出,那么父进程将一直阻塞,等待子进程的退出。

wait是阻塞等待,需要等到子进程退出。

如果子进程不退出,父进程默认在wait的时候,调用这个系统调用的时候,也就不返回,默认叫做阻塞状态。

子进程的退出结果我们如何得知呢?
pid_t waitpid(pid_t pid, int * status , int options )

第一个参数可以传递不同的值,pid = -1 ,等待任一子进程与wait等效。

pid > 0,等待进程ID与pid相等的子进程。

pid = 0 等待的条件还没有就绪

waitpid的返回值是等待的那个进程的pid。

*第二个参数int status:

这是一个输出型参数,期望通过指针将函数内部的资源带出来 。

这个int是被当作几部分使用的。

在这里插入图片描述

低的8个比特位表示进程的代码是否出了异常。进程异常其实就是收到了信号。形如kill- l 的这些信号

在这里插入图片描述

这里的次八个比特位,表示退出状态。就是我们的退出码。

WIFEXITED(status) 检查进程退出时是否出异常了

WEXITSTATUS(status) 若WIFEXITED(status)非零,提取子进程的退出码。

这两个是宏。

在这里插入图片描述

第三个参数:option

option设置当前等待的方式,默认是0,也就是阻塞等待的方式。

纯阻塞式调用就是父进程一直等待子进程的结束,父进程什么也不做。

第二种等待方式:非阻塞轮询。WNOHANG

非阻塞轮询是一直在问子进程是否结束。

非阻塞轮询+自己的事情。让我们父进程可以做其他的事情。

父进程投递到等待队列,就是子进程的等待队列,就是子进程PCB内维护的。

非阻塞轮询 + 自己的事情 WNOHANG

waitpid实时监测子进程状态,第三个参数为0表示父进程正在阻塞,在等待子进程退出。

任务不要太长,子进程退出是也不是立马回收,等待其他子进程一起回收。

子进程等待过程当中,父进程的任务应该简单。

父进程一定是最后一个退出的进程

等待过程中,可以将曾经创建的子进程进行释放。

父进程一定是最开始运行的

进程等待的原理:

任意进程都有自己的退出信息。

父进程通过waitpid函数来检查子进程的退出信息。 在这里插入图片描述

wait一次只能等待一个进程结束,如果有多个子进程,需要用for循环进行调用wait,将每个进程都回收

核心工作就是读取子进程的task_struct,内核数据结构对象。

操作系统也会检测你等待的是否是你的子进程

程序替换

进程替换的原理

程序替换不创建新进程,只进行进程的程序代码和数据的替换工作。

execl 函数的原理如下:

  1. 加载新程序: 当调用 execl 时,它会加载一个新的程序映像到当前进程的地址空间中。这个新程序会完全取代当前进程的内容,包括代码段、数据段等。
  2. 清理当前进程: 调用 execl 后,操作系统会清理当前进程的内存空间,释放旧程序所占用的资源。这包括清除当前进程的代码、数据和堆栈等内容。
  3. 加载新程序内容: execl 接受要执行的新程序的路径和参数作为参数,并用它们替换当前进程的内容。新程序的路径指定了要执行的可执行文件,而后续的参数指定了新程序的命令行参数。
  4. 执行新程序: 一旦 execl 成功执行,控制权就会移交给新程序。新程序会从其入口点开始执行,而当前进程则被完全替换,不再存在于系统中。

这种 exec 系列函数的特性使得进程可以在运行时动态地切换到其他程序,从而实现了程序的连续执行和功能的扩展。通常,execl 函数会在子进程中调用,因为在调用成功后,当前进程的内容将被完全替换,包括代码和数据,不再是原始的父进程。****

可变参数列表

!在这里插入图片描述

第一个参数是文件路径,第二个是执行的指令。

程序替换把别人的程序替换一下后面的程序不跑

exec系列的函数,只有失败返回值,没有成功返回值

Linux中形成的可执行程序,是有格式的,ELF,可执行程序的表头,可执行程序的入口地址。

execl第一个参数找到该程序。

第二个第三个参数是找到程序之后,接下来的操作

execlp

不用带路径,会在环境变量中查找路径。自己会在默认的PATH环境变量中查找。第一个参数直接写文件名字即可。

execv v代表的是vector

v改变的是第二个参数,不再是一个可变参数,而是一个字符数组的形式传入。

在linux系统中,所有的进程启动都是由execv系列函数来启动的。

exec函数承载的是加载器的功能。

承担着把数据从磁盘加载到内存的功能。

execvp不用加PATH,直接告诉文件名就行。

execle 第一个参数是程序路径,第二个是指令,第三个是环境变量

e是环境变量,代表可以传输自己的环境变量。

环境变量是什么时候给进程的?

环境变量也是数据

他们也在地址空间当中

创建子进程的时候,环境变量就已经被子进程继承下去了,所以程序替换过程中,环境变量不会被替换。

所以我如果想给子进程传递环境变量,该怎么传递?

1 新增环境变量

在父进程的空间直接putenv

enevle 传递环境变量,直接采取的是覆盖。

2 彻底替换环境变量

execve 系统调用接口

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值