1、单进程版 – 最简单的先看看程序替换
现象就是
1、我们用自己的进程封装了内置指令ls,并且代码中execl 后 printf 的after并没有打印出来。
2、谈进程替换的原理
单进程替换基本原理
上面例子中execl的做法非常简单粗暴,要调用ls,那么就把mycommand这个进程的代码和数据进行替换,物理空间根据ls的大小进行调整,左边进程地址空间和页表做出相应的调整,然后从新加载的程序main函数入口处重新执行,而并没有创建新的进程!
3、多进程版 – 验证各种程序替换接口
子进程execl调用了ls,父进程也等待成功了,并且没有创建新进程
问题1、在子进程中执行execl会不会影响到父进程呢?如果不会的话为什么?
答:不会,写时拷贝技术,并且进程互相具有独立性
问题2.、在单继承原理说了,替换是简单粗暴的把可执行程序在物理内存中代码和数据
直接替换,那么fork之后创建的子进程把数据进行写时拷贝了,那么代码呢?它也有写时拷贝吗?
答案:是的,操作系统去写入这个代码,他发现这个代码是父子共享的,所以不能直接替换,所以代码也要进行写时拷贝。
所以写时拷贝不一定只在数据层面,代码上也体现。
问题3、程序替换有没有创建新的子进程?
没有,可以看到调用execl前后的子进程活着和死了之后都是同一个进程Pid
不创建新进程,只进行进程的程序代码和数据的替换工作!
如果是父子场景发生写时拷贝,不是父子直接替换(单进程场景)
适当的改改页表映射关系,进而让pcb执行新程序。
补充小知识:
1、现象:程序替换成功之后,exec后续的代码不会被执行,替换失败呢?才可能执行后续代码exec函数,只有失败返回值,没有成功返回值!! 替换成功后也没有地方返回
2、我们的CPU如何得知程序的入口地址?
这个入口还不是main函数而是类似CRTmain
Linux中形成的可执行程序,是有格式的,可执行程序不是杂乱无章的把二进制往里一放,有ELF,可执行程序的表头,可执行程序的入口地址就在表中!! !
所以刚开始一定要把表头先加载到内存当中。
验证接口
先看看execl和execlp的接口如何使用
execv
ls是一个可执行程序,它有main函数,它的命令行参数是从myargv[]传入的,即便是list也是要转换为argv[]字符串指针数组传入给ls的 main函数
linux中所有的进程都一定是别人的子进程
命令行中所有的进程都是bash的子进程
所有的进程在启动的时候都是采用exec系列函数启动执行的
exce* 函数承担的是加载器的效果!
目前要知道 我们是可以把命令行参数传递给可执行程序的
就像bash中输入命令ls 我是要启动ls这个进程?
问题:如果我们的exec能够执行系统命令,能不能执行我们自己的命令呢?? ?
答:
bash和python3 是解释器 底层都是C/C++ ,用于解释脚本Or py语言
这些语言运行起来,本质都是进程,被操作系统调度
exec 是加载器,你要运行就要使用
验证mycommand给自己写的程序传入的命令行参数和环境变量
问题:替换程序是否会替换环境变量信息?
创建子进程时,子进程就已经继承了父进程的环境变量数据,在地址空间栈区之上
那么子进程即使不传main函数的env形参 也能拿到环境变量
但是进程替换时并不会替换环境变量信息。所以程序替换你可以替换代码和数据但环境变量不会替换
运行结果看到确实没有替换环境变量
问题又来了
所以我如果想给子进程传递环境变量,该怎么传递???
1、新增环境变量
第一种,在bash中export设置一个新的环境变量,那么子进程替换的时候就可以拿到新增的环境变量
第二种,父进程的地址空间中直接putenv!
要想与bash的环境变量无关,就想让父进程导一个环境变量再传递给子进程,就利用putenv , putenv添加一个环境变量到调用进程的上下文,也就是谁调用就给谁导
与bash无关
第三种,我非得传递环境变量,那么就利用第三方变量environ,用前需声明,可以不在main写形参env
2、彻底替换 - 自定义环境变量
4、总结
exec*函数的关系
左边六个库函数的区别:只是传参的不同 最终都转化为execve系统调用
5、自定义shell(媒婆)
bash(王婆) 为了不砸自己招牌,创建了子进程,这样子进程崩了就不会影响Bash
bash如何创建子进程呢?
答:fork()
我们上面写的代码不就是fork创建子进程,只不过命令是在代码中硬编码写好的-a -b -c,如果我把命令行参数以命令行形式传进入,动态的让他去执行命令,那它不就是一个shell了吗
问题:我们shell本身的环境变量是从哪里来的???