目录
进程程序替换
替换原理
编辑替换函数
函数解释
命名理解
函数使用
execl
execlp
execv
execvp
调用其它程序
进程程序替换
替换原理
用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动,进程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。
替换函数
其实含有六种以exec开头的函数,统称exec函数
#include <unistd.h>
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[]);
上面这些函数可以通过我们传递的参数,进行程序替换。、
- const char *arg:可执行程序的命令行参数(注:要以NULL结尾,表示参数传递结束)
- const char* path:可执行程序的位置
- const char *file: 可执行程序的名称
- char *const argc[ ]:可执行程序命令行参数形成的指针数组
- char *const envp[ ]:环境变量
函数解释
- 这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。
- 如果调用出错则返回-1
- 所以exec函数只有出错的返回值而没有成功的返回值
命名理解
- l(list) : 表示参数采用列表
- v(vector) : 参数用数组
- p(path) : 有p自动搜索环境变量PATH
- e(env) : 表示自己维护环境变量
函数使用
execl
我们知道在Linux系统中的指令其实也是一个程序,因此我们可以将我们的指令使用上面的函数进行替换。理论可行,实践操作。
1 #include<stdio.h>
2 #include<unistd.h>
3 int main()
4 {
5 printf("pid : %d ,exec command begin\n",getpid());
6 execl("/usr/bin/ls","ls","-a","-l",NULL);
7 printf("pid: %d , exec commamd after\n",getpid() );
8 return 0;
9 }
现象:
- 在main函数中,使用exec函数执行了我们Linux下的ls指令
- main函数中最后一句话没有执行
对现象的解释:
当我们运行我们编写的可执行程序时,操作系统首先会自动生成PCB结构体、进程地址空间(虚拟地址)、页表;将我们可执行程序中的代码通过页表一一对应到物理地址中。当我们执行到exec函数时,操作系统又将页表对应的物理空间中的内容替换成为exec中所要替换的程序。
程序替换成功后,就开始执行替换成功后的程序。前面我们讲过每个程序都有一个eip数据(进程运行计数器寄存器),因此会从开始执行替换后的程序;前面的文章我们又说过每个进程都会有退出码代表程序完成的怎么样;所以下面的一句话就不会执行。或者也可以这样理解:程序替换成功后原来的代码都会被覆盖掉。
总结:
- 第二个参数其实就是我们在Linux终端怎么执行的指令,将各个部分拆开作为函数的参数。
- 并不会创建新的进程。
execlp
1 #include<stdio.h>
2 #include<unistd.h>
3 int main()
4 {
5 printf("pid : %d ,exec command begin\n",getpid());
6 execlp("ls","ls","-a","-l",NULL);
7 // execl("/usr/bin/ls","ls","-a","-l",NULL);
8 printf("pid: %d , exec commamd after\n",getpid() );
9 return 0;
10 }
execv
1 #include<stdio.h>
2 #include<unistd.h>
3 int main()
4 {
5 printf("pid : %d ,exec command begin\n",getpid());
6 char* const argv[]={
W> 7 "ls",
W> 8 "-a",
W> 9 "-l",
10 NULL
11 };
12 execv("/usr/bin/ls",argv);
13 // execlp("ls","ls","-a","-l",NULL);
14 // execl("/usr/bin/ls","ls","-a","-l",NULL);
15 printf("pid: %d , exec commamd after\n",getpid() );
16 return 0;
17 }
~
execvp
1 #include<stdio.h>
2 #include<unistd.h>
3 int main()
4 {
5 printf("pid : %d ,exec command begin\n",getpid());
6 char* const argv[]={
W> 7 "ls",
W> 8 "-a",
W> 9 "-l",
10 NULL
11 };
12 execvp(argv[0],argv);
13 // execv("/usr/bin/ls",argv);
14 // execlp("ls","ls","-a","-l",NULL);
15 // execl("/usr/bin/ls","ls","-a","-l",NULL);
16 printf("pid: %d , exec commamd after\n",getpid() );
17 return 0;
18 }
调用其它程序
上面我们使用exec函数调用Linux下的指令,指令也是程序。我们写的可执行程序也是程序那么我们能否使用exec函数来替换我们自己写的可执行程序呢?理论可行,实践操作。
我们在同一目录下编写一个简单的C++程序,编译好的可执行程序也在这个目录下,尝试替换这个C++程序。
//test.cpp
1 #include<iostream>
2 using namespace std;
3 int main()
4 {
5 cout<<"hello C++"<<endl;
6 cout<<"hello C++"<<endl;
7 cout<<"hello C++"<<endl;
8 cout<<"hello C++"<<endl;
9 cout<<"hello C++"<<endl;
10 return 0;
11 }
//test2-8-1.c
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 //child
12 printf("pid :%d ,exec command begin \n",getpid());
13 sleep(3);
14 execl("./mytest1","mytest1",NULL);
15 printf("pid: %d ,exec command after\n",getpid());
16 }
17 else
18 {
19 pid_t rid = waitpid(-1,NULL,0);
20 if(rid>0)
21 {
22 printf("wait success ,rid : %d \n",rid);
23 }
24 }
25 return 0;
26 }
结论:
- 可以通过exec函数替换我们自己编写的可执行程序。
- 其实不仅可以调用C++编写的程序,也可以调用shell、python、Java。编写的脚本/可执行程序因为不管什么语言编写的可执行程序,执行起来对于操作系统来说就是进程;所以可以使用exec进行各种语言编写程序的替换。
- exec这一批函数其实就是在操作系统中进程的加载器 。
还有一个包含环境变量参数的函数,我会在下篇文章给大家介绍;并且在下篇文章中带大家完成一个我们自己的shell。
今天对Linux下进程的替换的分享到这就结束了,希望大家读完后有很大的收获,也可以在评论区点评文章中的内容和分享自己的看法;个人主页还有很多精彩的内容。您三连的支持就是我前进的动力,感谢大家的支持!!!