1.替换原理
用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。

我们可以先复习一下进程地址空间的相关知识
cpu执行进程时,会通过页表映射到其代码和数据

当我们进行进程替换时,即使用替换函数执行ls时,ls会用它的代码和数据替换原本物理内存中的代码和数据,(程序替换并不会创建新的进程),页表及其左侧的部分不变,只需要略微调整。(因为这两部分已经是解耦合了)

小知识:那么我们是如何得知程序的入口地址?即使我们知道mm_struct中代码区的位置在哪,但是我们怎么准确知道具体程序开始执行的位置呢 。
答:Linux中形成的可执行程序,是有格式的,即第一幅图中的ELF格式,可执行程序的入口地址就储存在可执行程序的表头中
(前面还有个表头没标出来)

我们可以先不加载代码和数据,但是得要先把表头加载到内存里 ,代码的进程地址空间里是有代码起始地址start,终止地址end,各种数据区开头的数字就是从这个表中来的
小知识:同时编译多个文件
使用伪目标all,all 可以依赖后面数个文件,但是没有指明依赖方法,因此依赖方法不执行
如果不使用all,自顶向下先扫描到哪个,就编译哪个了。

2.替换函数
其实有六种以exec开头的函数,统称exec函数:
#include <unistd.h>`
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ...,char *const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execve(const char *path, char *const argv[], char *const envp[]);
3.函数解释
1.这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。
2.如果调用出错则返回-1
3.所以exec函数只有出错的返回值而没有成功的返回值。

第一个参数就是找到位置,第二个参数就是进行相应的操作,命令行参数怎么写就这里就怎么写
(要运行otherExe时,我们在命令行会采用./otherExe,但是这里的./实质上就是指明这个可执行程序在本目录,因此我们填第一个参数找位置的时候就自然可以省略./)
最重要的是,替换函数实质上就是相当于一个加载器 。
任何语言只要想跑就要变进程,只要变进程就要用这个加载器
4.命名理解
l(list) : 表示参数采用列表
v(vector) : 参数用数组
p(path) : 有p自动搜索环境变量PATH
e(env) : 表示自己维护环境变量

exec调用举例如下:
#include <unistd.h>
int main()
{
char *const argv[] = {"ps", "-ef", NULL};
char *const envp[] = {"PATH=/bin:/usr/bin", "TERM=console", NULL};
execl("/bin/ps", "ps", "-ef", NULL);
// 带p的,可以使用环境变量PATH,无需写全路径
execlp("ps", "ps", "-ef", NULL);
// 带e的,需要自己组装环境变量
execle("ps", "ps", "-ef", NULL, envp);
execv("/bin/ps", argv);
// 带p的,可以使用环境变量PATH,无需写全路径
execvp("ps", argv);
// 带e的,需要自己组装环境变量
execve("/bin/ps", argv, envp);
exit(0);
}

(使用数组无非是把所有的命令行参数放到一个数组里然后传进去,本质相同)
事实上,只有execve是真正的系统调用,其它五个函数最终都调用 execve,所以execve在man手册 第2节,其它函数在man手册第3节。这些函数之间的关系如下图所示
下图exec函数族 一个完整的例子:

关于环境变量的参数,我们可以不填是因为环境变量也是数据,创建子进程时,子进程会继承父进程的环境变量,并且进程被替换后,环境变量不会被替换 。(不主动传参情况下)
那如果我们想给子进程传递环境变量,怎么传递?
1.新增环境变量 ---可以在父进程的地址空间中直接putenv就能新增环境变量了,再创建子进程时就能被继承。.

2.彻底替换
如果选择了要传环境变量的接口,我们可以直接获取父进程的环境变量,然后传入
我们也可以自定义环境变量然后传入

同时,系统采用的方式是覆盖原环境变量,而不是追加
(替换程序里面写了代码print命令行参数和环境变量)

运行mycommand
1610

被折叠的 条评论
为什么被折叠?



