exec函数族
- fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的ID并未改变。
- exec只是用磁盘上的一个新程序替换了当前进程的正文段、数据段、堆栈和栈段。
- 有7种不同的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[]);
int fexecve(int fd, char *const argv[], char *const envp[]);
函数解释
- 这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。
- 如果调用出错,则返回-1.
- 所有exec函数只有在出错的时候才有返回值,如果成功,则没有返回值。
命名解释
- 这些函数前4个字母都是一样的,只有后面的几个字母是不一样的,因此我们只要掌握规律就可以很好的掌握它。
- l(list):表示参数采用列表
- v(vector):参数用的数组
- p(path):函数后有p的会自动搜索环境 变量PATH
- e(env):表示自己维护环境变量
函数名 | 参数格式 | 是否带路径 | 是否使用当前环境变量 |
---|---|---|---|
execl | 列表 | 不是 | 是 |
execlp | 列表 | 是 | 是 |
execle | 列表 | 不是 | 不是,需要自己组装环境变量 |
execv | 数组 | 不是 | 是 |
execvp | 数组 | 是 | 是 |
execve | 数组 | 不是 | 不是,需要自己组装环境变量 |
execlp函数
- 加载一个进程,借助PATH环境变量
int execlp(const char *file, const char *arg, ...);
成功:无返回;失败:-1
- 参数1:要加载的程序的名字。该函数需要配合PATH环境变量来使用,当PATH中所有目录搜索后没有参数1则出错返回。
- 该函数通常用来调用系统程序。如:ls、date、cp、cat等命令。
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<stdlib.h>
4
5 int main()
6 {
7 pid_t pid;
8 pid = fork();
9 if(pid < 0)
10 {
11 perror("fork error");
12 exit(0);
13 }
14 else if(pid > 0)
15 {
16 sleep(2);
17 printf("I am parent !\n");
18 }else
19 {
20 execlp("ls","ls","-a","-l",NULL);
21 }
22 return 0;
23 }
- execlp(“ls”,“ls”,"-a","-l",NULL);这个函数有一个需要注意的地方,我们看到第二个参数也是“ls”,其实传入给的是argv[0],这个参数在可以任意改动,不会出错,原因是这个参数并不参与函数内部的使用。
execl函数
- 加载一个进程, 通过 路径+程序名 来加载。
int execl(const char *path, const char *arg, ...);
成功:无返回;失败:-1
- 对比execlp,如加载"ls"命令带有-l,-F参数
execlp("ls", "ls", "-l", "-F", NULL); 使用程序名在PATH中搜索。
execl("/bin/ls", "ls", "-l", "-F", NULL); 使用参数1给出的绝对路径搜索。
- 代码和上面的代码只是换了20行代码。
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<stdlib.h>
4
5 int main()
6 {
7 pid_t pid;
8 pid = fork();
9 if(pid < 0)
10 {
11 perror("fork error");
12 exit(0);
13 }
14 else if(pid > 0)
15 {
16 sleep(2);
17 printf("I am parent !\n");
18 }else
19 {
20 execl("/bin/ls","ls","-a","-l","-h",NULL);
21 }
22 return 0;
23 }
- 可以看到效果是一样的
execvp函数
- 加载一个进程,使用自定义环境变量env
int execvp(const char *file, const char *argv[]);
-
变参形式:
- ①…
- ② argv[] (main函数也是变参函数,形式上等同于
- int main(int argc, char *argv0, …))
-
变参终止条件:① NULL结尾 ② 固参指定
-
execvp与execlp参数形式不同,原理一致。
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<stdlib.h>
4
5 int main()
6 {
7 pid_t pid;
8 pid = fork();
9 if(pid < 0)
10 {
11 perror("fork error");
12 exit(0);
13 }
14 else if(pid > 0)
15 {
16 sleep(2);
17 printf("I am parent !\n");
18 }else
19 {
20 char* ergv[] = {"ls","-a","-l","-h",NULL};
21 execvp("ls",ergv);
22 }
23 return 0;
24 }
exec函数调用例子
1 #include<stdio.h>
2 #include<unistd.h>
3
4 int main()
5 {
6 char* const argv[] = {"ps","-ef",NULL};
7 char* const envp[] = {"PATH=/bin:usr/bin:","TERM=console",NULL};
8
9 execl("/bin/ps","ps","-ef",NULL);
10
11 //带p的,可以使用环境变量,无须写全路径
12 execlp("ps","ps","-ef",NULL);
13
14 //带e的。需要自己组装环境变量
15 execle("ps","ps","-ef",NULL,envp);
16
17 execv("/bin/ps",argv);
18
19 //带p的,可以使用环境变量PATH,无须写全路径
20 execvp("ps",argv);
21
22 //带e的,需要自己组装环境变量
23 execve("/bin/ps",argv,envp);
24
25 return 0;
26 }
- 事实上,只有execve是真正的系统调用,其他的五个函数最终都调用execve,所以execve在man手册上的第2节,其他函数在man手册的第3节。