前言:
在未进行进程的程序替换时,父子进程的数据是独立的通过页表进行映射进行实现进程数据的独立性,但是父子进程的代码还是共享的,我父进程将子进程进行创建出来不仅仅只会有父子进程只进行执行共享代码的需求,有的时候需要让子进程进行单独的去执行独立的一份代码,此时这种情况下是如何进行设计的呢?通过了解进程的程序替换就可以豁然开朗。
什么是进程的程序替换?
进程程序替换的该概念和原理
进程的程序替换就是让子进程和父进程执行不同的代码,实现父子进程数据和代码的全部独立,父子进程代码的独立性也是通过页表的重新进行映射实现的。代码仅从重新加载到内存中通过页表重新建立映射关系,程序地址替换也完成了代码的写实拷贝。
进程的程序替换是如何进行的?
通过exec系列的函数进行完成的。
exec系列的函数实际上就是将磁盘中的代码和数据进行加载到内存中形成进程。
查看进程替换函数
man 3 exec
进程替换函数的解析
进程替换函数的返回值是只有出现错误时才会返回-1,进行进程替换成功时是没有返回值的。为什么进程替换成功会没有返回值,我们在下面进行解释。
下面都是通过
execl(const char* path, const char*arg, ...)
这里的l是list的意思
参数说明
path是进程替换后想要进行执行新程序的路径,这里可以是绝对路径和相对路径(要进行执行一个新的程序首先需要先进行找到程序,因此需要首先需要进行传路径)
arg是函数的参数列表,... 代表代表参数的可变参数列表,可以根据传入的参数进行自动识别参数的个数,第一个参数一般是程序名,后续通常是这个程序的参数列表,最后以NULL进行结尾。
当子进程通过程序替换函数进行程序替换时,execl函数后面的代码是没有被子进程进行执行的,子进程在执行程序替换时,程序替换函数后面的代码就被替换掉了,被替换掉的包括execl程序替换函数的返回值,这就是为什么进程替换成功后,进程替换函数没有返回值的原因,也是父进程进行进程等待拿到子进程退出码依然是0的原因。
当通过exec系列函数进行进行调用python语言时的文件时,首先需要进行传入python解释器的路径,然后是解释器的名称,接着是文件名,最后还是以NULL进行结尾。
execv(const char* path, const char*argv[ ])
execv函数和execl函数进行对比区别只有程序进行时传递给新程序的参数列表的方式不同,这里‘v’是代表vector的意思,通过数组进行接收传递的参数列表
execlp(const char* file, const char*arg, ...)
这里和execl相比,这里函数名中带来一个p,这里的p是PATH的意思,是环境变量,所以说当新的程序路径放到环境变量中时,不需要进行传地址,只需要进行传环境变量名即可
execvp(const char* file, const char*argv[])
直接通过环境变量的来进行查找解释器的位置
execle(const char* path, const char*arg, ...
...,char* const envp[ ])
execve(const char* filename , char *const argv[ ] , char* const envp[ ])
execve这个函数和上面的函数是不同的,execve
是最基础的 exec
系统调用,它直接向操作系统请求替换当前进程的映像。其他的 exec
函数(如 execl
, execp
, execvp
, execv
等)通常是对 execve
的封装,提供了更加方便、简洁的接口。
模拟实现shell程序外壳程序
框架的设计思路:
首先shell外壳程序是始终不退出的,需要进行一个死循环,然后通过两个数组进行存储我们键盘输入的命令,其中cmd_line用于存储未被打散的整个命令,注意这个cmd_line数组的类型是char类型,用char类型的原因是存储整个命令方便后续gets进行读取,用g_argv数组用于存储打散的字符串。通过memset进行初始化cmd_line数组,通过fgets函数进行在流输入(键盘中)读取完整命令存在于cmd_line数组中,fgets函数读取完整命令时我们通过回车键进行命令的完成也会被读取到,通过将'\n'→'\0'防止空格一行的出现,然后通过strtok函数将cmd_line函数中的完整命令打散放入g_argv中,然后通过fork函数进行创建子进程运用程序替换进行执行操作。
myshell程序外壳的框架(代码的实现)
一些疑惑的解决
为什么要通过创建子进程让子进程通过程序替换进行执行命令,而不是直接通过父进程进行执行命令???
首先这里进行执行命令不都是系统命令,也有可能是我们编写的程序通过编译后形成的可执行程序的运行,回顾一下我们在window系统下,通过IDE进行编写代码时,都经常出现代码跑挂了的情况,如果让父进程直接执行命令,IDE进程就直接挂掉了,给我们的主观感受就是进程闪退了,再比如我们来leetcode进行刷算法题时,当我们的代码写崩了时,leetcode进程也没有崩掉,也就是说我们进行执行OJ 的也是leetcode父进程创建的子进程。
为什么通过cd ..命令时不会进行当前路径的移动??
因为cd是内部命令,执行内部命令的是shell外壳程序本身,也就是父进程执行内部命令,子进程是没有进行进行执行cd 命令的,并且pwd是属于外部命令,通过pwd执行的是子进程的路径,所以说pwd进行执行时路径是未发生变化的。
处理方法:
如何做到支持命令的简写
例如"ls -l"→"ll"
子进程是如何进行继承父进程的环境变量的??