目录
替换原理
用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。
通过代码看现象
linux下执行以下代码:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
int main()
{
printf("testexec ... begin!\n");
execl("/usr/bin/ls","ls","-l","-a",NULL);
printf("testexec ... end!\n");
return 0;
}
其实有六种以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[]);
int execvpe(const char *file, char *const argv[],char *const envp[]);
原理解释
所以只打印了第一个printf,用ls替换了老的代码和数据,走到第二个printf时已经执行exec了早就替换了所以看不到第二个printf 。过程中没有创建新进程,只不过用老进程的壳子执行新程序的代码。exec*函数执行完毕后,后续的代码不见了因为被替换。
所以不用关心execl函数的返回值因为只要替换成功就不会往后执行,反之只要继续执行了那么就是没替换成功则替换失败。
代码换成多进程版
fork创建子进程,让子进程自己去替换,父进程在等待。
创建子进程:让子进程完成任务:1.让子进程执行父进程代码的一部分 2.让子进程执行一个全新的程序。
子进程程序替换后不影响父进程因为进程具有独立性,会发生写时拷贝在内存开辟空间覆盖代码和数据
使用替换方法并了解含义
函数解释
1. 这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。
2. 如果调用出错则返回-1
3. 所以exec函数只有出错的返回值而没有成功的返回值。
命令理解
1. l(list) : 表示参数采用列表
2. v(vector) : 参数用数组
3. p(path) : 有p自动搜索环境变量PATH
4. e(env) : 表示自己维护环境变量
int execl(const char *path, const char *arg, ...);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execlp(const char *file, const char *arg, ...);
int execvpe(const char *file, char *const argv[],char *const envp[]);
makefile:1 .PHONY:all 2 all:myprogram testexec 3 4 testexec:testvim.c 5 gcc -o $@ $^ -std=c99 6 myprogram:myprogram.cc 7 g++ -o $@ $^ -std=c++11 8 .PHONY:clean 9 clean: 10 rm -rf mytest.exe
1 #include<iostream> 2 #include<unistd.h> 3 using namespace std; 4 int main (int argc,char*argv[],char*env[]) 5 { 6 int i=0; 7 for(;argv[i];i++){ 8 printf("argv[%d] : %s\n",i,argv[i]); 9 } 10 printf("-----------------------------\n"); 11 for(i=0;env[i];i++){ 12 printf("env[%d] : %s\n",i,argv[i]); 13 } 14 printf("-----------------------------\n"); 15 cout<<"hello c++,i am a program"<<getpid()<<endl; 16 cout<<"hello c++,i am a program"<<getpid()<<endl; 17 return 0; 18 }
1 #include<stdio.h> 2 #include<string.h> 3 #include<stdlib.h> 4 #include<unistd.h> 5 #include<sys/wait.h> 6 #include<sys/types.h> 7 int main() 8 { 9 printf("testexec begin \n"); 10 pid_t id=fork(); 11 if(id==0){ 12 char *const argv[]={ 13 (char*)"myprogram", 14 (char*)"-a", 15 (char*)"-b", 16 NULL 17 18 }; 19 char*const envp[]={ 20 (char*)"HAHA=1111", 21 (char*)"HEHE=2222", 22 NULL 23 }; 24 printf("child pid:%d\n",getpid()); 25 sleep(2); 26 execvpe("./myprogram",argv,envp); 27 exit(-1); 28 } 29 int status=0; 30 pid_t rid=waitpid(id,&status,0); 31 if(rid>0){ 32 printf("farther wait succes ,child exit code:%d\n",WEXITSTATUS(status)); 33 } 34 printf("testexec end!\n"); 35 return 0; 36 }
当然也可以传bash进程传给父进程的environ
通过exec*函数可以让子进程执行另一个程序。也可以让子进程执行另一个可执行文件
execl("./program","program",NULL); program为另一个程序的可执行文件
但是值得注意的是只有execve才是系统调用,执行的是二号手册(man)
可以理解为其他exec*函数接口底层都调用execve系统调用。将其他exec*函数封装是为了适应不同的应用场景。