进程替换
替换原理
在前面我们在使用 fork 函数创建子进程的时候,往往和父进程执行的是相同的程序。但是实际在我们的期望中,子进程应该是执行和父进程不同的代码。所以此时,子进程往往要调用 exec函数从而执行另一个程序。调用 exec 并不创建新的进程,所以调用 exec 函数并不会改变该进程的 pid。
被替换的用户的代码和数据完全被新的程序替换,并从新的程序的启动例程开始执行。
替换函数
exec函数族
exec 函数族其中总共六种,非别是:
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[]);
这些函数,如果调用成功则从新加载的程序的启动例程开始执行,不返回。
调用失败,返回 -1 。
这些函数的名字也是有一定规律的
- l (list) : 表示参数使用列表
- v (vector) : 参数使用数组
- p(path) : 自动搜索环境标量
- e(env) :需要自己去维护环境变量
execl 函数
int execl(const char *path , const char *arg , ...);
// path 是指所要执行的文件路径
这个函数的后缀是 l,所以参数使用列表的形式,但是要注意,最后一个参数必须使用 NULL 来进行结尾。
在这里我们没有创建一个子进程,而是对进程直接进行替换,从而执行 ls -l 指令,而 ls 指令是在 /bin/ls 中存放的。
execlp 函数
int execlp(const char *file , const char *arg , ...);
后缀是 p,可以使用环境变量PATH,所以无需写全要调用函数的路径。
execlp 会从 PATH 环境变量所指的目录中查找符合参数 file 的文件名,找到然后执行该文件的 argv[0],argv[1]….。
注意这里的参数 NULL 不能用 0 来替换。
在替换后,execlp 会根据 PATH 变量中的 /bin 找到 /bin/ls,然后执行。
execle 函数
int execle(const char *path , const char *arg, ... , char * const envp[]);
后缀是 e 所以我们需要自己维护一份环境变量。
这份我们自己维护的环境变量是用来传递给替换后的新程序的,所以本程序依然需要写上绝对路径。
execv 函数
int execv(const char* path, char* const argv[]);
后缀是 v,所以参数应该为数组。
参数 argv 也应该由 NULL 来结尾 。
execvp 函数
int execvp(const char* file, char* const argv[]);
后缀为 p,所以我们可以直接使用环境变量中的 PAHT
由于 ls 是在 /bin/ls 中存放的,所以我们可以直接使用环境变量 PATH 中的 /bin 目录。
execve 函数
后缀是 e 所以需要我们自己去维护环境变量,同 execle。
事实上,只有execve 是真正的系统调用,其他五个函数最终都调用 execve。