1.为啥需要进程程序替换
因为父进程创建出来的子进程和父进程拥有相同的代码段,所以,子进程的代码和父进程是一样的。当我们想要让子进程执行不同的程序时候,就需要让子进程调用进程程序替换的接口,从而让子进程执行不一样的代码。
2.原理:替换进程的数据段和代码段,更新堆栈。
3.exec函数簇
3.1 execl函数
int execl(const char* path,const char* argv, ...);
参数:
path:带路径的可执行程序。
arg:传递给可执行程序的命令行参数,(...表示可变参数列表),第一个参数是可执行程序本身,如果需要传递多个参数,则用“,”进行间隔,末尾以NULL结尾。
返回值:
函数如果调用成功,则加载新的程序从启动代码开始执行,不在返回;如果调用出错则返回-1
调用成功:
#include <stdio.h>
#include <unistd.h>
int main(){
printf("in the main,proecss begin...\n");
execl("/usr/bin/pwd","pwd",NULL);
printf("if run here,the execl failed\n");
return 0;
}
调用失败:
#include <stdio.h>
#include <unistd.h>
int main(){
printf("in the main,proecss begin...\n");
execl("pwd","pwd",NULL);
printf("if run here,the execl failed\n");
return 0;
}
3.2 execlp函数
int exedlp(const char * file ,const char * arg,...);
参数:
file:可执行程序,可以不用带有路径,也可以带有路径。
arg:传递给可执行的程序的命令行参数,第一个参数,需要可执行程序本身,如果需要传递多个参数,则用“,”进行间隔,末尾以NULL结尾。
返回值:函数如果调用成功则加载新的程序从启动代码开始执行,如果调用出错则返回-1。
>1file参数不带路径
#include <stdio.h>
#include <unistd.h>
int main(){
printf("in the main,proecss begin...\n");
execl("pwd","pwd",NULL);
printf("if run here,the execl failed\n");
return 0;
}
>2 file参数带路径
#include <stdio.h>
#include <unistd.h>
int main(){
printf("in the main,proecss begin...\n");
execl("/usr/bin/pwd","pwd",NULL);
printf("if run here,the execl failed\n");
return 0;
}
为什么execlp,第一个参数不用带有路径呢?
原因就是:execlp这个函数会去搜索PATH这个环境变量,看看要替换的可执行程序是否在PATH环境变量对应的路径下能不能找到。能找到:正常替换,执行替换的程序。没找到:报错返回,替换失败了。
环境变量PATH里有/usr/bin这个路径
可执行程序不存在:
#include <stdio.h>
#include <unistd.h>
int main(){
printf("in the main,proecss begin...\n");
int ret=execlp("abcde","pwd",NULL);
printf("if run here,the execl failed,%d\n",ret);
return 0;
}
总结:
函数名当中带有" l ”︰传递给可执行程序的参数是以可变参数列表的方式进行传递。第一个参数,需要可执行程序本身,如果需要传递多个参数,则用“,”进行间隔,末尾以NULL结尾。
函数名当中带有“ p ”:可以使用环境变量PATH,无需写全路径。换句话说,函数会搜索环境变量PATH,找到可执行程序,所以不用写路径。
3.3 execle
int execle(const char *path, const char *arg,.. .,char *const envp[ ]);
参数:
path:带路径的可执行程序(需要路径)
arg:传递给可执行的程序的命令行参数,第一个参数需要可执行程序本身,如果需要传递多个参数,则用“,”进行间隔,末尾以NULL结尾。
envp :程序员传递环境变量,换句话说,程序员调用该函数的时候,需要自己组织环境变量传递给函数。
返回值:
函数如果调用成功则加载新的程序从启动代码开始执行,如果调用出错则返回-1。
总结:
函数名当中带有“e”:程序员传递环境变量,换句话说,程序员调用该函数的时候,需要自己组织环境变量传递给函数。
#include <stdio.h>
#include <stdlib.h>
int main(){
printf("%s:%d\n",__FILE__,__LINE__);
printf("%s\n",getenv("PATH"));
return 0;
}
#include <stdio.h>
#include <unistd.h>
int main(){
printf("in the main,proecss begin...\n");
extern char** environ;
int ret=execle("/home/sy/work/usually/test","test",NULL,environ);
printf("if run here,the execl failed,%d\n",ret);
return 0;
}
参数environ没有被设置
#include <stdio.h>
#include <unistd.h>
int main(){
printf("in the main,proecss begin...\n");
extern char** environ;
int ret=execle("/home/sy/work/usually/test","test",NULL,NULL);
printf("if run here,the execl failed,%d\n",ret);
return 0;
}
3.4 execv函数
int execv(const char *path,char *onst argv[ ]);
参数:
path:带路径的可执行程序(需要路径)
argv :传递给可执行的程序能命令行参数,以指针数组的方式进行传递。第一个参数,需要可执行程序本身,多个参数就都放到数组当中!末尾以NULL结尾。
返回值:
函数如果调用成功则加载新的程序从启动代码开始执行,如果调用出错则返回-1。
总结:
函数名字当中带有“v”:说明命令行参数是以字符指针数组的方式进行传递的,字符指针数组需要程序员自己进行定义。
#include <stdio.h>
#include <unistd.h>
int main(){
printf("in the main,proecss begin...\n");
char* argv[10]={NULL};
argv[0]="ls";
argv[0]="-a";
argv[0]="-l";
argv[1]=NULL;
int ret=execv("/usr/bin/ls",argv);
printf("if run here,the execl failed,%d\n",ret);
return 0;
}
3.5 execvp函数
int execvp(const char* file,char *const argv[ ]);
参数:
file:可执行程序,可以不用带有路径,也可以带有路径。
argv :传递给可执行的程序的命令行参数,以指针数组的方式进行传递。第一个参数,需要可执行程序本身,多个参数就都放到数组当中末尾以NULL结尾。
返回值:
函数如果调用成功则加载新的程序从启动代码开始执行,如果调用出错则返回-1。
#include <stdio.h>
#include <unistd.h>
int main(){
printf("in the main,proecss begin...\n");
char* argv[10]={NULL};
argv[0]="ls";
argv[0]="-a";
argv[0]="-l";
argv[1]=NULL;
int ret=execvp("ls",argv);
printf("if run here,the execl failed,%d\n",ret);
return 0;
}
3.6 execve函数
int execve(const char *path, char *const argv[ ], char *const envp[ ]);
参数:
path:带路径的可执行程序(需要路径)。
argv :传递给可执行的程序的命令行参数,以指针数组的方式进行传递。第一个参数,需要可执行程序本身,多个参数就都放到数组当中,末尾以NULL结尾。
envp :程序员传递环境变量,换句话说,程序员调用该函数的时候,需要自己组织环境变量传递给函数。
返回值:函数如果调用成功则加载新的程序从启动代码开始执行,如果调用出错则返回-1。
#include <stdio.h>
#include <unistd.h>
int main(){
printf("in the main,proecss begin...\n");
char* argv[10]={NULL};
argv[0]="ls";
argv[0]="-a";
argv[0]="-l";
argv[1]=NULL;
extern char** environ;
int ret=execve("/usr/bin/ls",argv,environ);
printf("if run here,the execl failed,%d\n",ret);
return 0;
}
3.7 这些函数原型看起来很容易混,但只要掌握了规律就很好记。
l(list) : 表示参数采用列表
v(vector) : 参数用数组
p(path) : 有p自动搜索环境变量PATH
e(env) : 表示自己维护环境变量
3.8进程程序替换+fork+进程等待
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main(){
pid_t pid=fork();
if(pid<0){
printf("process creat failed\n");
}
else if(pid == 0){
//child
printf("i am child,exec beginning\n");
execl("/usr/bin/ls","ls","-a","-l",NULL);
printf("if run here,failed\n");
}
else{
//father
printf("i am father,waiting...\n");
wait(NULL);
}
return 0;
}