目录
进程程序替换
引入
之前说过,fork有两种用法(让子进程执行父进程的其他模块代码 / 执行新的程序)
执行父进程的其他代码很简单,直接把代码给他就行
而执行新的程序需要由程序替换完成
程序替换
概念
在一个正在运行的进程中将[当前执行的程序映像]替换为[另一个程序的映像]
程序映像
指一个正在运行的程序在内存中的表示
它包括了程序的代码、数据、堆栈以及与程序执行相关的各种资源
原理
- 这个过程会将新程序的代码和数据加载到原有进程的地址空间和物理内存中,然后将页表重新建立映射
- 一旦新的地址空间被设置好,操作系统会将控制权转移到新的代码中的main函数或程序的入口点
- task_struct中的数据不会被替换/重置,会根据新的可执行文件更新其中的信息
- 所以新进程可以使用原有进程的资源和环境
- 所以,实际上进程替换并没有创建一个新的子进程
- 因为进程=代码数据+内核数据结构,它并没有创建数据结构,而是直接替换掉
如何实现 -- exec系列函数
介绍
- 可以启动一个新程序,取代当前进程的执行映像,将当前进程的地址空间、堆栈、文件描述符等内容替换为新程序的内容
- 原来的程序将被终止,而新程序将从
main
函数开始执行- 允许在新程序中传递命令行参数、环境变量以及控制文件描述符的重定向等信息
execl函数
首先执行一个程序,需要找到它在哪里,然后才是怎么执行它
path
- 新程序的所在路径(相对路径 / 绝对路径 都可以)
arg
- 一个字符串,作为新程序的第一个参数(通常是程序的名称)
- ... :可变参数列表,用于传递给新程序的命令行参数,这些参数是以空指针 NULL 结尾的字符串列表
示例 -- ls
可以看到,替换后,成功执行了ls命令
- 当调用execl函数后,新程序的代码就会替换掉原有程序(全部都会被替换,包括他自己)
- 因此不会打印end(已经没有啦)
- 调用之前还是按照原有进程执行
- 所以beginning打印了,替换对它的打印结果没有影响
带参数 -- ls -l
- 平常我们如何在命令行执行程序,就如何填充arg(需要的选项直接添加即可,但必须以NULL结尾)
- 可以看到,我们使用这个方法调用的ls,打印出来的目录没有平时的绿色
- 实际上我们平时使用的ls是默认有--color=auto选项的,所以打印出来的文件名有自己的颜色
- 我们也可以将这个选项加入写的程序中:
--color=auto
`
返回值
仅在替换失败时返回-1
- 如果替换成功,返回值没有意义,因为原有程序会被全部替换,返回了也接收不到
- 但如果替换失败,返回值就有意义了(或者如果它执行之后的代码,也就代表它替换失败了)
- 如果它的退出码为222,则代表它替换失败
示例 -- 子进程执行替换
上面的代码是在父进程内部替换的,如果创建一个子进程去完成呢?
![]()
- 可以看到,父进程的代码并不受影响,它的工作就是创建子进程,等待子进程执行完毕进行处理(它相当于领导,为手下人指派工作,等待他们完成)
- 如果我们想要这样的效果,就可以让子进程进行程序替换
作用 -- 完成父子进程的分离
这里在进行进程替换时,也要发生写时拷贝,子进程载入一个新进程的时候,也是写入啊
- 之前的学习中,我们的写时拷贝只针对 当数据需要做修改时
- 而现在代码也会被修改,因此代码也要发生写时拷贝(不然父进程会被影响,不满足独立性)
- 所以进程替换就实现了 父子进程在代码和数据上彻底分离!
execv函数 -- 指针数组
- path:和execl相同,是程序的路径
- 但是传入执行方法的参数不同
- execl是将字符串直接写在参数列表中,execv将字符串放在指针数组中
execlp函数 -- path
带p的函数其实会 自动在环境变量PATH中保存的路径下寻找 ,因此系统命令不需要带路径
路径只需要写要执行的命令名就好了
execvp函数
其实就是把execv和execp两个函数的特性合起来了
execle函数
envp参数
其中前两个参数哦我们已经熟悉了,但envp又是什么呢?
是环境变量:
- 用于给要执行的进程传递环境变量
- 可以让我们自定义(实际上就是指针数组,也需要以NULL结尾)
- 也可以是父进程main函数中的环境变量(这样就可以让环境变量具有全局属性,被子进程继承了)
- 但上文一直使用的是系统命令,无法看到我们传入的环境变量,因此,我们需要将该进程替换为我们自己写的程序
示例
使用execle为test程序提供环境变量,在test中使用getenv来打印该环境变量:
execve函数 -- 系统调用
实际上,这个函数才是最底层的系统调用,上面那一系列的函数是在它之上封装出的
封装出的那么多函数,可以用于满足用户不同的调用场景
执行其他语言的进程
如果我们写了一个python的代码,是否可以使用exec系列函数执行呢?
示例
- 这里与其他不同的是,使用Python编写的程序叫做python可执行脚本文件
- 我们要执行的python代码,实际是在python这个程序(解释器)的里面被解释->执行的
- 所以要执行的程序名是python
- 执行的文件名作为第二个参数
总结
上面这么多函数,其实就是这几个字母进行组合
而exec系列函数其实就是加载器的底层接口(它什么类型的代码都可以帮助执行)
加载器
- 解析可执行文件的格式、加载它们到内存中,并为程序的执行提供必要的资源和环境