进程程序替换
- 替换概念
- 替换函数
- execl()
- execv()
- execvp()/execlp()
- execle()/execvpe()
- 如何在C/C++程序里面执行别的语言写的程序。
- 小tips
替换概念
当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的代码部分开始运行。调用exec并不创建新的进程,所以调用exec前后该进程的id并未改变。
大概意思就是程序替换只是把程序换了,仅仅是从1号程序换到了2号程序,修改了页表的映射地址,但是并没有创建新的PCB,因此没有创建新的进程。
这就是进程替换的概念!
替换函数
在linux手册里,有6个相似的函数,他们虽然参数列表不一样,但是功能一样。
- 如果这些函数调用成功则加载新的程序,从启动代码开始执行,不再返回。
- 如果出错则返回-1。
- 所以exec函数只有出错的返回值而没有成功的返回值。
execl()
l:列表的形式传入参数
int extcl(const char * path, const char* argv,…);
参数:
path:是要打开可执行程序的路径
argv,… :这个语法表示可变的参数列表,可以传入多个不定个数的参数,最后一个参数传入NULL,表示参数传递完成。
描述不清楚,看个例子就明白了:
1 #include<stdio.h>2 #include<unistd.h>5 int main()6 {7 printf("当前进程开始!\n");8 //execl("/usr/bin/ls","ls","--color=auto","-l",NULL);9 //一旦调用成功,后续代码都不会执行了!!! 10 execl("/usr/bin/ls","ls","--color=auto","-l", "-i",NULL);11 //execl("/usr/bin/top", "top", NULL);12 printf("当前进程结束!\n"); 13 return 0; 14 }
因为Linux系统下一切皆文件,ls命令其实就是一个可执行程序,我们使用execl函数,执行这个程序。我们可以改变可变参数列表来使用ls命令不同的功能。
execv()
v:数组的形式传入参数
int extcv(const char * path, char *const argv[]);
参数:
path:仍然是文件路径。
char *argv[]:指针数组,以数组的形式传入。
为了更加适应实际的应用场景,我们使用父进程fork子进程,然后使用子进程进行进程替换,这样不会影响父进程的执行。
如下:
7 #define NUM 328 int main()9 {10 pid_t id = fork();11 12 if(id == 0)13 {14 //子进程15 // ls -a -l16 printf("子进程开始执行,pid = %d \n",getpid());17 //传入指针数组18 char *const _argv[NUM] = {19 (char*)"ls",20 (char*)"-l",21 (char*)"-i",22 NULL23 };24 execv("/usr/bin/ls",_argv); //传入指针数组!!!25 exit(1);26 }27 else{28 //父进程29 printf("父进程开始等待,pid =%d \n", getpid()); 30 int status = 0;31 pid_t ret = waitpid(-1, &status, 0); //阻塞等待32 if(ret>0)33 {34 printf("等待成功,退出码为:%d \n", WEXITSTATUS(status));35 }36 }37 return 0;38 }
可以看到,子进程帮助父进程完成了执行ls命令,并且没有影响父进程的执行。
execvp()/execlp()
p:表明第一个参数不再是文件路径,而是通过环境变量就可以找到的程序。
看下面的例子更能清楚的解释:
execvp()的例子:
7 #define NUM 328 int main()9 {10 pid_t id = fork();11 12 if(id == 0)13 {14 //子进程15 // ls -a -l16 printf("子进程开始执行,pid = %d \n",getpid());17 //传入指针数组18 char *const _argv[NUM] = {19 (char*)"ls", 20 (char*)"-l", 21 (char*)"-i", 22 NULL 23 }; 24 //execv("/usr/bin/ls",_argv); 25 execvp("ls",_argv); //因为环境变量的存在,我们可以只输入ls便可找到该文件。 26 exit(1); 27 } 28 else{ 29 //父进程 30 printf("父进程开始等待,pid =%d \n", getpid()); 31 int status = 0; 32 pid_t ret = waitpid(-1, &status, 0); //阻塞等待 33 if(ret>0) 34 { 35 printf("等待成功,退出码为:%d \n", WEXITSTATUS(status)); 36 } 37 } 38 return 0;39 }
execlp()例子:
仅仅将上面的代码,25行换成26行的内容即可!
25 //execvp("ls",_argv); //因为环境变量的存在,我们可以只输入ls便可找到该文件。 26 execlp("ls", "ls", "-l", "-i", NULL);
execle()/execvpe()
e:可以维护自己的环境变量,将环境变量传给要替换的进程。
以execle为例:
将上面的子进程调用部分修改:
7 #define NUM 328 int main()9 {10 11 //设置一个环境变量,为的是 让子进程拿到12 //因为环境变量具有全局属性13 char *const _env[NUM]={14 (char*)"xty=123456789",15 NULL16 };31 execle("./myexe", "mycmd", "-a", NULL, _env); //修改成这样,//其中列表参数是随便传的,myexe是调用的可执行程序。
myexe:1 #include<stdio.h>2 #include<stdlib.h>3 4 int main()5 {6 7 printf("得到的环境变量xty:%s\n", getenv("xty"));8 return 0;9 }
可知myexe获取到了环境变量。
如何在C/C++程序里面执行别的语言写的程序。
//使用这个命令即可运行
python test.py
首先写一个python文件脚本。待会我们就是用c程序执行它。
使用execlp()调用比较合适。
1 #include <stdio.h>2 #include<stdlib.h>3 #include<unistd.h>4 #include<sys/types.h>5 #include<sys/wait.h>6 int main()7 {8 pid_t id = fork();9 if(id == 0)10 {11 //子进程,调用别的语言写的程序12 execlp("python", "python", "test.py", NULL);13 exit(-1);14 }15 else{16 //父进程17 int status = 0;18 printf("父进程开始等待: \n");19 int ret = waitpid(-1, &status, 0); //阻塞等待20 if(ret > 0)21 {22 printf("父进程等待成功,子进程退出码为:%d \n",WEXITSTATUS(status));23 24 }25 } 26 return 0;27 }
我们可以看出python脚本被我们成功调用!
小tips
我们在运行py脚本时,需要运行python这个程序。因为python是一个解释器,我们需要使用解释器来解释test.py文件。
python test.py
如果我们给该test.py文件加上执行(x)权限,那么我们这样即可运行该文件:
因此将上面的execlp调用参数,修改为下面这样也可以:
//12 execlp("python", "python", "test.py", NULL);12 execlp("./test.py", "test.py", NULL);
上面就是程序替换的相关知识,更深入的内容还请读者自行查阅和学习。