上篇文章:Linux:进程控制(创建/终止/等待/获取退出信息/多进程)
目录
1.阅读程序替换代码
fork()之后,父子各自执行父进程代码的一部分,如果子进程想执行一个全新的程序,就要通过进程的程序替换来完成。
程序替换是通过特定的接口,加载磁盘上的一个全新的程序(代码和数据),加载到调用进程的地址空间中。


运行结果:

1.1execl

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

总结:程序替换是只替换代码和数据,不创建新进程,而是让进程执行一份全新的代码。所以它的pid并不会改变。
而当使用fork()函数,让子进程程序通过程序替换时,由于进程具有独立性,那么就会发生写时拷贝,父子两个进程会彻底的独立。

观察上述的运行结果,我们发现它并没有打印最后一句,原因是程序替换一旦成功,就没有返回值,如果有返回值,就是替换失败。

3.父子进程版本

通过上述代码,可以让子进程完成一个全新的任务,而这,不就是bash的工作原理吗?我们这里的父进程就相当于bash。
4.使用举例

l表示参数使用列表

六处细节

1.省略输入有时候也能运行,但是不建议
2.path:不建议省略路径,写绝对或者相对路径
3.我们自己写的程序也能被替换->如果当前进程不fork子进程,execl就是加载程序到内存的过程->execl就是加载器的底层接口
4.程序替换并没有创建新进程
示例:



5.父进程或者OS传递给子进程的是通过系统调用exec*传递给子进程的



6.通过上述示例,可以看出我们通过系统调用,让进程以全新的程序进行,所以也可以通过C语言调用C++,shell,python,java这类语言->最终原因是因为其本质都是进程
以shell脚本为例:


修改myexec.c


以python脚本为例:



5.替换函数
前提认识:
要执行一个程序,其首先时找到特定路径下的文件,之后再根据文件或选项进行执行,例如执行ls命令,应该是先找到它的路径再做选择:
有七种以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[]);
int execvpe(const char *file, char *const argv[],char *const envp[]);

5.1execv
int execv(const char *path, char *const argv[]);
此函数后面的参数使用的是指针数组传参,可以以数组的形式调用某些函数。
这里的v指vector

5.2execlp
int execlp(const char *file, const char *arg, ...);
p指PATH,只需要传递可执行文件名,execlp会自动到环境变量PATH中查找可执行程序。

5.3execvp
int execvp(const char *file, char *const argv[]);

5.4execle
e(environ):表示自己维护环境变量(自定义环境变量)
int execle(const char *path, const char *arg, ...,char *const envp[]);

我们自己创建环境变量:


**结论:**进程程序替换的时候,即使我们没有显示的传递环境变量表的信息,但是子进程依旧能够通过main(char *env[])获得默认的环境变量。
我们也可以使用系统默认的环境变量:


5.5execve
int execve(const char *path, char *const argv[], char *const envp[]);
execve()是一个真正的系统调用,操作系统提供的程序替换系统调用,就这一个函数。
EXEC(3)是库函数

而上方的库函数最终会转换为execve,所以进程程序替换的时候,即使我们没有显示的传递环境变量表的信息,但是子进程依旧能够通过main(char *env[])获得默认的环境变量。

思考
那么针对execve为什么要生成这么多的封装呢?
因为它的接口形参设置上比较麻烦,要求参数全部设为数组,但我们所面对的环境变量不一定全为数组设置,所以为了满足不同的应用场景才产生如此多的封装。

6.拓展
在保留旧的环境变量的同时,新增几个环境变量
6.1putenv

putenv是指在当前进程的环境变量表中,新增一组环境变量

结果:

本章完。
