1,原型
前面我们提到,可以用fork创建一个子进程。但是,这个子进程与原先的父进程共享程序段。也就是说两个进程执行的是同一段代码。这好像并没有太大的用处。正常来说,我们希望的不是创建的新进程可以执行完全不同的功能吗?这要怎么做呢?
这就要用到我们今天讲的exec系列函数。
通常的做法是,先用fork起一个子进程,再调用execXXX函数将子进程替换成一个指定的程序。这样狸猫换太子之后,新的程序就获得了原先子进程的pid和父进程。完全顶包了原先的子进程。
exec系列共有7个函数。今天的例子以最简单的execl为例子。7个函数的原型如下:
#include<unistd.h>
int execl(const char* pathname, const char* arg0, ...); //arg以空指针结尾
int execv(const char* pathname, char* const argv[]);
int execle(const char* pathname,const char* arg0, ...);//arg以空指针结尾,再接环境变量
int execve(const char* pathname, char* const argv[], char* const envp[]);
int execlp(const char* filename, const char* arg0, ...); //arg以空指针结尾
int execvp(const char* filename, char* const argv[]);
int fexecve(int fd, char* const argv[], char* const envp[]);
返回值:出错返回-1,成功不返回。
2,例子:
2.1 hello程序
先装备好一个简单的hello程序做狸猫,便于待会被exec用来换太子。
先新建一个hello.c:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[]){
printf("Hello");
if (argc > 0){
printf(", %s!\n", argv[0]);
} else {
printf("\n");
}
}
它只是简单滴打印第一个输入参数,并在前面加上一个hello而已。
将其编译成hello,就放在当前目录下。
2.2 入正题
有了狸猫,开始准备太子。在study_Linux.c下写入以下代码。
int main(){
pid_t pid;
//用fork产生一个与自己一模一样的子进程
if((pid = fork()) < 0) {
printf("fork error!\n");
exit(1);
} else if(pid == 0){ //注意,是在子进程内调用execl
//将子进程替换成hello,并给hello传递输入参数Bihan,注意最后一个输入参数一定是NULL
if(execl("./hello", "Bihan", NULL) < 0){
printf("execle error!\n");
exit(1);
}
}
printf("parent process\n");
//这里waitpid还能等到hello,说明hello的pid就是原先子进程的pid,
//父进程就把hello当自己的子进程,狸猫换太子成功
if(waitpid(pid, NULL, 0) < 0){
printf("wait error!\n");
exit(1);
}else{
printf("wait end\n");
}
return 0;
}
执行结果:
➜ code ./study_linux
parent process
Hello, Bihan!
wait end
可以看到hello被执行,输入参数也被传递,最后hello终止时父进程还能wait到。
注意一个细节。通常,当我们在命令行里调用hello的时候,比如这样:
➜ code ./hello Bihan
则hello的main函数的argv[0]并不是Bihan,而是“./hello”,当前程序名。所以刚才的执行效果是这样的:
➜ code ./hello Bihan
Hello, ./hello!
而我们在exec中指定hello的输入参数时,argv[0]就是“Bihan”,没有当前程序名自动成为argv[0]这一说法了。
3,7个exec之间的关系
注意:只有execve是系统调用,其他的只是库函数。其他6个最终也是通过调用execve实现的。
这组函数的名字是有规律的:
- 除了最后一个,其他都以exec开头;
- l代表list,表示狸猫的输入参数要一个一个列出来;与之相对,v代表vector,表示狸猫的输入参数是个数组;
- e代表environment,环境 变量,表示重新设定狸猫的环境变量而不使用当前的环境变量;
- p代表exec的第一个输入参数不是路径而是文件名,其会在环境变量path的指定下去寻找该文件名的文件,p代表path;
- 最后一个的f代表文件描述符。所以总结起来指定狸猫有文件名、路径名、文件描述符三种方法。