进程的程序替换,exec 函数族。
fork 创建子进程后执行的是和父进程相同的程序,如果需要他执行不同的代码分支,子进程旺旺需要调用一种 exec 函数以执行另一个程序。当进程调用一种 exec 函数时,该进程的用户空间代码的数据完全被新程序替换,从新程序的启动例程开始执行。调用 exec 并不创建新的进程,所以调用 exec 前后该进程的 id 并未改变。
当前进程的 .text 和 .data 替换为所需要加载的程序的 .text 和 .data,然后让进程从新的 .text 第一条指令开始执行,进程的 id 不变,换核子不换壳子。
exec 函数族的一些参数有些不同,底层实现原理一样。
SYNOPSIS
#include
extern char **environ;
int execl(const char *path, const char *arg0, ... /*, (char *)0 */);
int execle(const char *path, const char *arg0, ... /*, (char *)0, char *const envp[] */);
int execlp(const char *file, const char *arg0, ... /*, (char *)0 */);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvP(const char *file, const char *search_path, char *const argv[]);
事实上,只有 execve 是真正的系统调用,其他五个函数最终都调用 execve,所以 execve 在 man 手册第 2 节,其他函数在 man 手册的第 3 节。这些函数之间的关系如下图:
加载一个进程,通过 路径+程序名 来加载。
int execl(const char *path, const char *arg0, ... /*, (char *)0 */);
**返回:**成功:无返回;失败:-1。
**参数:**path:可执行程序的路径,如/bin/ls
。后面跟要传给这个程序的参数。
有了这个函数就可以来加载一个自定义的程序了。
栗子:
#include
#include
#include
int main(void) {
pid_t ret = fork();
if (ret == -1) {
perror("fork error");
exit(-1);
} else if (ret == 0) {
// 子进程
execl("/bin/ls", "ls", "-a", NULL);
} else {
// 父进程
sleep(1);
printf("father process\n");
}
return 0;
}
加载一个进程,借助 PATH 环境变量。
int execlp(const char *file, const char *arg0, ... /*, (char *)0 */);
**返回:**成功:没有返回;失败:-1。
**参数:**file:要加载的程序名。该函数需要配合 PATH 环境变量来使用,当 PATH 中所有目录搜索后没有参数 file 则返回出错。后面跟传给要执行程序的参数。
该函数通常用来调用系统程序。如:ls、date、cp、cat 等。
栗子:
#include
#include
#include
int main(void) {
pid_t ret = fork();
if (ret == -1) {
perror("fork error");
} else if (ret == 0) {
// 子进程
execlp("ls", "ls", "-al", NULL); // 注意第二个参数 ls 对应到main函数的参数就是argv[0],传给ls其实他并没有使用这个参数
} else {
// 父进程
sleep(1);
printf("father process\n");
}
return 0;
}
最后一个参数是一个数组,自己的环境变量数组,这个数组必须以 NULL 结尾。
int execle(const char *path, const char *arg0, ... /*, (char *)0, char *const envp[] */);
int execv(const char *path, char *const argv[]);
栗子:
#include
#include
#include
int main(void) {
pid_t ret = fork();
if (ret == -1) {
perror("fork error");
exit(-1);
} else if (ret == 0) {
// 子进程
char* argv[] = {"ls", "-a", NULL};
execv("/bin/ls", argv);
} else {
// 父进程
sleep(1);
printf("father process\n");
}
return 0;
}