目录
上期,我们学习了进程创建,进程终止和进程等待,今天我们要学习的是进程控制中相对重要的板块------进程替换。
进程替换的概念
在进程创建时,我们使用了fork函数创建了子进程。但是大家仔细回想一下,其实在子进程创建之后,父子进程共享的是同一代码,可以理解为子进程执行的是父进程代码的一部分,有没有什么办法可以让子进程不去执行父进程的代码,而去执行一份新的代码呢?进程替换就是其中的方法之一。
进程替换:进程替换是指替换到原有进程的代码,但是原有的进程的数据结构不变的技术,就叫做进程替换。
替换原理图示如下。
由图示可见,进程替换其实是没有新进程的创建的,只是更改了之前进程代码以及虚拟地址空间和页表之间的对应关系。所以进程替换的代价相对而言是比较小的。
进程替换的函数
进程替换主要要用到exec类的函数,用于去执行另一个程序的代码。exec函数主要有六个,我们一一来解释。
execl
参数:第一个参数为要执行的命令的路径,第二个参数为命令行参数,即命令要执行什么,但是最终必须以NULL结尾,不然会出错。
先看下述代码。
这是一个简单的打印程序。运行结果如下:
然后使用execl函数进行替换。
#include<stdio.h>
#include<unistd.h>
int main()
{
printf("process begin\n");
execl("/usr/bin/ls","ls","-a","-l",NULL);
printf("process end\n");
return 0;
}
运行结果如下。
我们惊奇的发现,"process end"字符串没有打印,这是为什么呢?
原因就是在进行进程替换之后,当前进程的所有代码都被进行了替换,之前的和之后的代码都被进行了替换,所以替换之后的代码自然没有被执行,所以自然没有打印对应的语句。
execlp
参数列表:第一个参数为要执行的命令的名称,第二个参数为命令函参数,与execl要求类似。
代码如下。
#include<stdio.h>
#include<unistd.h>
int main()
{
printf("process begin\n");
execlp("ls","ls","-a","-l",NULL);
printf("process end\n");
return 0;
}
运行结果如下。
我们发现运行结果也符合我们的预期。
execle
参数列表:第一个参数为命令的路径,第二个参数为命令行参数与execl类似,第三个参数为环境变量参数。
代码如下。
#include<stdio.h>
#include<unistd.h>
int main()
{
printf("process begin\n");
char *env[]={
"hello world",
"hello world",
"hello yjd"
};
execle("/usr/bin/ls","ls","-a","-l",NULL,env);
printf("process end\n");
return 0;
}
运行结果如下。
运行结果符合预期。
execv
参数列表:第一个参数为命令的路径,第二个参数为命令行参数组成的一个指针数组。
代码如下。
#include<stdio.h>
#include<unistd.h>
int main()
{
printf("process begin\n");
char* const argv[]={
"ls",
"-a",
"-l",
NULL
};
execv("/usr/bin/ls",argv);
printf("process end\n");
return 0;
}
运行结果如下。
运行结果符合预期。
execvp
参数列表:第一个参数为命令名称,第二个参数为命令行参数组成的指针数组。
代码如下。
#include<stdio.h>
#include<unistd.h>
int main()
{
printf("process begin\n");
char* const argv[]={
"ls",
"-a",
"-l",
NULL
};
execvp("ls",argv);
printf("process end\n");
return 0;
}
运行结果如下。
运行结果符合预期。
execve
这个接口是系统调用接口,其它六个exec类函数都是库函数接口,证明其它六个exec函数与execve函数肯定是有关系的,具体什么关系我们等下再去讨论。
参数列表:第一个参数为命令路径,第二个参数为命令行参数组成的指针数组,第三个参数为环境变量组成的指针数组。
代码如下。
#include<stdio.h>
#include<unistd.h>
int main()
{
printf("process begin\n");
char* const env[]={
"hello world",
"hello world",
"hello yjd",
NULL
};
char* const argv[]={
"ls",
"-a",
"-l",
NULL
};
execve("/usr/bin/ls",argv,env);
printf("process end\n");
return 0;
}
运行结果如下。
运行结果如何预期。
回到刚开始的问题execve函数与其它几个函数有什么区别呢?
可以理解为除过execve函数之外的其它函数的本质都是execve函数,因为execve函数是系统调用接口,而除execve之外的其它函数都是第三方库的接口,我们知道库函数的实现本质上是系统调用函数的实现。
以上便是本期进程替换的所有内容,到了这里进程控制的所有内容已经全部学习完毕。
本期内容到此结束^_^