进程替换
假如操作系统正在执行某一个程序,我们可以利用程序替换函数指定一个新的程序,让操作系统去执行我们新指定的程序。也就是这样一种情形下,我们fork一个进程,如果fork成功,子进程会和父进程执行相同的代码,而我们创建子进程是希望子进程执行指定的操作,这个要实现在本程序复制之后执行其它程序的过程,我们叫做进程替换
要实现进程替换,我们就要使用exec系列函数
execl
execlp
execle
execv
execvp
execve
其中,替换函数都是以exec开头,后面不同的字符例如:l、v、p、e代表不同的使用方法,一般有如下规律:
l(list) : 表示参数采用列表
v(vector) : 参数用数组
p(path) : 有p自动搜索环境变量PATH
e(env) : 表示自己维护环境变量
其中只有exeve为系统调用,剩下的函数都是调用exeve
我们在Linux中用man命令可以查看exec系列函数族
关于execl系列函数,只是从声明来看,参数中是需要路径
而execv系列函数则是不需要路径的
当系统执行单个execl函数,执行一个新的进程时
对应的虚拟地址和物理地址和磁盘对应的关系图如下
理论部分介绍没有办法深刻理解,我们通过对exec系列函数使用进一步理解
execl
这个是不添加fork的单一使用的exec的使用
可以看到execl函数的使用是 路径+参数+结尾标记
这个结尾标记的使用是必须的
(char*)NULL
NULL和\0,0的意义相同,但是(char*)的强转的一定不能少的
单单看到这一步是无法理解execl的使用的
我们对这个程序进行编译运行
可以看到这里运行了我们函数中声明的ps -f命令
那么在程序中为什么没用输出 parent voer!!!
这里我们就要和命令就行 对比来说明
如果我们在终端中给一个错误的命令
这里就是在提示我们错误的原因
那么我们如果在刚刚的main程序中也给出错误的参数
那么结果会如何呢
这里我们把参数改成lss
看看编译运行后会有什么不同的结果
我们可以看到最下面就是fork运行的结果,那么可以看到给出了错误的结果还运行了execl后面的代码
这里就涉及了execl的底层运行
如果给出的参数正确,那么代码到execl处就会结束
如果给出的参数不正确,才会执行execl下方的代码
那么这里我们还看到一个新的输出函数 perror
这个其实和之前学习的标准错误类似
这个函数其实也可以用printf代替,但是用perror可以给出错误的结果
在后续的学习中,我们也可以将perror经常运用
然后还有一个点 这是第一次遇见
我们对main函数进行改变
我们运行编译之后再分析结果
可以看到我们这里替换的是系统的ls -a命令
我们再执行一下系统的ls -a命令
这里看到系统的ls -a对于可执行程序的颜色是不同于我们替换的ls -a的颜色的
那这是怎么回事呢
其实这是ls的底层执行
实际上我们使用系统的命令执行的ls 是 ls --color
这里其实是命令的参数执行的规则 --后面会解析为一个单次 例如ls --color
-后面会解析为单个命令 例如 ps -ef
然后我们修改execl的参数
就可以达到和系统一样的效果
这样就可以实现和系统一样的效果
当然我们也可以调用我们自己编写的可执行程序
比如这里我们编写一个简单的main程序
前提是要编译出可执行程序
可以看到这里就也可以替换成我们自己编译的main程序
execl和fork结合
我们之前使用的execl都是单独使用
这里我们将execl和fork结合使用
pid=0 说明是子进程
那我们看看这个程序在运行时会有什么问题
这里可以看到没有什么问题
我们将程序放到后台运行
这里就可以看到
子进程比父进程先结束,产生了僵尸进程现象
不了解僵尸进程可以翻看往期博文
Linux——僵尸进程,文件操作_iccoke的博客-CSDN博客
那么我们肯定要解决这个僵尸进程问题
我们使用wait子进程等待父进程结束并给出退出码
我们这里并不要求获取子进程的退出码,所以我们只用wait即可
可以看到我们解决了僵尸进程问题
接下来我们可以介绍剩下的几个函数
在execl的基础下理解就容易的多
execlp和execvp
这两个使用就是不用带上路径
然后execl类 和execv类区别就是 execv的参数储存在数组中
而execle和execve都是会加上一个环境变量
用envp数组存储
这里就涉及到信号问题,我们下一节再讨论