进程替换的背景:
进程的替换我们需要调用execl这个接口,exxecl在3号手册,属于系统接口。
调用系统命令
execl
为了方便理解execl的作用,我们写一个程序:
单进程替换
我们发现运行结果是通过c库里的exec接口把系统命令 "ls"调用起来了,并且是ls带参的 ls-a-l-n形式
也就是 用ls替换了原本的程序。
替换原理
我们的程序跑起来之后就会变成进程,是进程就有PCB,地址空间,页表,物理内存。
同时ls命令也是一个文件,在磁盘当中。
它有代码和数据组成。
我们调用execl,就把ls的代码数据替换了我们原来的代码,数据,完成了进程替换:
多进程替换
我们让子进程进程ls替换,父进程以阻塞式等待子进程,等待成功把pid打印出来:
观察运行结果发现,子进程仍然没有打印出“进程结束这句话”
另外,父进程等待成功之后打印出来的pid和子进程的pid一样,因此得出结论:子进程没有创建成功。
为什么不创建子进程,因为子进程是父进程的拷贝,共享一块物理地址,子进程如果被替换,父进程也要被替换,为了进程的独立性,父进程不能被替换。
为什么这句话打印不出来呢?
原因:
证明:
我们故意把execl调用写错,这样调用失败就可以按照源程序走,就可以把源程序的后续显示了:
我们可以把execl的返回值用n接受一下,看一下execl替换失败之后会返回什么:
execl替换失败之后会返回-1:
我们利用这一特性可以这样写:执行替换,替换成功原程序就不会再执行,替换失败原程序就exit退出。
execlp
为什么要写两个ls,第一个ls是我们要替换的文件名,第二个ls是参数的一部分。
execv
写法:
execvp
因为自带path,即系统自己会在环境变量路径下查找,我们直接输要替换的目标文件名):
我们也可以这样:因为argv[0]就是ls这个参数。
上文验证了execl可以把系统文件调用起来替换我们原来文件,那是否可以调用我们自己写的文件来替换呢?
调用c++
写一个mytest.cc文件,让其输出三行hello word:
在test6.cc里面调用mytest.cc:
写一个makefile:
运行结果:
结论:execl不仅可以调用系统文件进行替换,也可以调用我们自己写的程序进行替换。
调用脚本
创建一个test.sh文件,在里面写一段脚本:
用系统解释器bash解释:
输入ll,可以看见t1,t2两个文件已经创建好了:
但是我们发现他们是没有可执行权限的,我们给它加上权限就可以像正常程序一样执行了:
我们在test6.cc文件里调用一下这个脚本文件:
发现就可以用这个脚本替换原来的程序。
调用Python
然后我们在test6.cc里调用
一个程序的创建,执行,结束流程:
首先 创建pcb,地址空间,页表,但是页表此时没有映射,也就进程创建好了,但是还没开始执行代码。
现在要执行代码了,系统再调用exec*类似的接口把代码和数据从磁盘中加载进内存里,然后填入页表,完成映射。
解疑:
全局变量在被编译时磁盘中就有数据了。
因为我们定义全局变量会赋初始值 这个初始值和全局变量会在磁盘.data区域提前预制好,当代码被加载进内存时就加载进去。
局部变量,堆栈上的变量只有被加载时,变为进程时才会有数据,才会形成堆区,栈区
定义的常量字符串,会被编译进可执行程序里的read only这一块区域,在程序被加载内存时会被加载进代码区和全局数据区之间的只读区域。