目录
一、进程替换
1.进程替换的概念
进程替换就是指将当前程序替换成一个新的程序,让当前进程执行这个新程序,如果替换成功了,原来的程序就不会被执行,也不会返回原来的返回值。
2.进程替换的原因
进程替换通常是在子进程中完成的,在学习进程替换之前,我们都是通过条件判断fork()的返回值使用子进程来执行父进程的一部分的代码,其实意义并不大。学习了进程替换可以使子进程执行一个全新的程序。
3.进程替换的本质
一个进程包含PCB,mm_struct(虚拟内存)等结构体,用来联系虚拟内存与物理内存的页表,以及程序的代码和数据。当进程中发生进程替换时,只有代码和数据发生替换,其他的内容不变。也就是说进程替换没有创建新的进程。
4.进程替换的方法
(1)函数的概述
进程运行起来时,它的代码和数据是要被加载到内存中的,是通过exec系列的函数来完成加载的。exec系列函数一共有七个,我们可以通过man手册来进行查询:
通过观察这几个函数我们发现它们都是在exec后面加入了几种后缀名。
可以通过后缀名的含义来进行区分记忆。
l:表示参数采用列表(以列表的方式一个一个传入进去)。
v:参数用数组。
p:有p自动搜索环境变量PATH(只要说名字就可以了,不用指明路径)。
e(env):表示自己维护的环境变量。(不用默认的环境变量)。
(2)execl
以l为后缀,说明以列表的形式传入参数。
int execl(const char *path, const char *arg, ...);
其中*path表示的是路径,arg表示的是要执行的陈鼓型,“...”表示的就是可变参数列表,及命令行上怎么执行就写入什么函数。必须以NULL作为参数列表的结束。
#include<unistd.h>
#include<stdio.h>
#include<sys/wait.h>
#include<stdlib.h>
int main()
{
if(fork()==0)
{
printf("command begin\n");
execl("/usr/bin/ls","ls","-a","-l",NULL);
printf("command fail\n");
exit(1);
}
waitpid(-1,NULL,0);
printf("wait child success\n");
return 0;
}
当执行子进程打印完“command begin”的语句的时候,进行进程替换。其中替换的是usr/bin/ls,在命令行要输入的是ls -a -l,将程序运行起来:
我们发现子进程被替换为了ls进程,并添加了-a -l等选项。
(3)execv
execv的后缀是v,指的是以数组的形式输出参数。
只需要将上面的execl替换为如下即可。
char* argv[]={"ls","-l","-a",NULL};
execv("/usr/bin/ls",argv);
其本质就是将参数列表放在了一个数组argv中。
(4)execlp
execlp("ls","ls","-l","-a",NULL);
就是将本来要指定的路径改成了通过环境变量进行查找。
(5)execvp
后缀为v和p,v表示以数组的形式,p表示的是不指名具体的路径去查找文件:
char* argv[]={"ls","-l","-a",NULL};
execvp("ls",argv);
(6)execle
后缀为l和e,e表示自己维护环境变量,不使用默认的环境变量。即向要执行的程序中导入环境变量:
建立两个程序一个程序完成进程替换,另一个程序为被替换的程序,负责打印自己的环境变量:
//mytest.c用来打印自身的环境变量
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main()
{
extern char** environ;
int i;
for(i=0;environ[i];i++)
{
printf("%d:%s\n",i,environ[i]);
}
}
//在myload.c中进行进程替换
char* env[]={"MYENV1=hahaha","MYENV2=hehehe",NULL};
execle("./mytest","mytest",NULL,env);
这里要注意的是已经指明了路径,在可变参数列表中可以不写入./
当我们不使用进程替换的时候,直接运行mytest,生成的是系统默认的环境变量:
如果通过进程替换执行mytest的话,它的环境变量就被定义成了env[],运行的结果是:
(7)execve
execve的代码同理:
char* argv[]={"mytest",NULL};
char* env[]={"MYENV1=hahaha","MYENV2=hehehe",NULL};
execve("./mytest",argv,env);
(8)execvpe
char* argv[]={"mytest",NULL};
char* env[]={"MYENV1=hahaha","MYENV2=hehehe",NULL};
execve("./mytest",argv,env);
由于p是在环境变量中找文件,那么就需要将我们新创建的文件添加到环境变量中,添加的方法为:
export PATH=$PATH:/home/用户名
此时执行的命令是:
其实所有的接口本质上无差别就是传递的参数不同而已,是为了满足不同的应用场景进行的封装。
操作系统实际上只提供了一个接口那就是:execve,其他的函数都是对该接口封装而成的库函数。它们的底层都是使用execve来进行实现的。