我们知道fork()之后,父子进程各自执行父进程的一部分代码,如果子进程就想执行一个全新的程序呢?
以前:父子代码共享,数据写时拷贝各自一份
如果子进程想要有自己的代码呢?
我们可以用进程的程序替换来完成以上功能。
程序替换:是通过特定的接口,找到磁盘上的一个权限程序(代码+数据),把它加载到调用进程的物理内存中。
原理:
所以想进行程序替换我们只要将新的磁盘上的程序加载到内存,并和当前的进程页表重新建立映射关系即可。
用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。
所以进程替换没有创建新的进程,因为OS并没有为该程序创建新的task_struct,用的全部都是之前程序的task_struct。
函数介绍:
可变参数列表:可以传入不定个数个参数。
我们在命令行上怎么执行的在这里就一个一个怎么填。
例如:ls -a -l 就直接填入"ls",‘’-a’',“-l”。
代码演示:
首先要知道ls的路径
#include <stdio.h>
#include <unistd.h>
int main()
{
printf("当前进程的开始代码\n");
execl("/usr/bin/ls","ls",NULL);
printf("当前进程的结束代码\n");
return 0;
}
运行结果对比
我们看到确实可以查出来
再多传几个查看一下
#include <stdio.h>
#include <unistd.h>
int main()
{
printf("当前进程的开始代码\n");
//execl("/usr/bin/ls","ls",NULL);
execl("/usr/bin/ls","ls","-a","-l",NULL);
printf("当前进程的结束代码\n");
return 0;
}
运行结果:
系统自身运行结果:
这里大家肯会有个一行为什么最后要传一个NULL因为execl函数是可变参数列表,所以我们要传一个NULL来告知它参数已经传递完毕。
这里大家肯会有疑惑为什么系统使用ls时查出来的信息是带颜色的,而自己查出来时并没有。
其实是系统起了别名:
我们知道多传递一个==–color=auto==参数列表即可
#include <stdio.h>
#include <unistd.h>
int main()
{
printf("当前进程的开始代码\n");
//execl("/usr/bin/ls","ls",NULL);
execl("/usr/bin/ls","ls","-a","-l","--color=auto",NULL);
printf("当前进程的结束代码\n");
return 0;
}
这里我就有一个问题了,我们代码后面不是有printf吗?为什么没有打印呢?
因为execl是程序替换,调用该函数成之后,会将当前进程的所有代码和数据都进行替换,包括已经执行的代码和未执行的代码。
所以一旦调用成功execl,后续所有代码,全部不会执行!
测试一下如果调用失败,我们后续代码会执行吗?
我们传入一个根本不存在的执行试一下
#include <stdio.h>
#include <unistd.h>
int main()
{
printf("当前进程的开始代码\n");
//execl("/usr/bin/ls","ls",NULL);
execl("/usr/bin/lss","lss","-a","-l","--color=auto",NULL);
printf("当前进程的结束代码\n");
return 0;
}
我们这里看到execl函数调用失败之后,确实后续代码成功的输出,并没有被替换。
**execl为什么调用成功了,为什么不用它的返回值呢?**因为根本就不需要,返回值毫无用处,execl调用成功是会把自己都替换掉。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
printf("当前进程的开始代码\n");
//execl("/usr/bin/ls","ls",NULL);
execl("/usr/bin/ls","ls","-a","-l","--color=auto",NULL);
exit(1);
printf("当前进程的结束代码\n");
return 0;
}
我们可以看到这里的退出码是0,而不是1.