磁力离心泵做网站,重庆百姓网,公司内部网站建设管理办法,网站开发技术汇总标题#xff1a;[Linux] Linux 进程程序替换 个人主页水墨不写bug #xff08;图片来源于网络#xff09; 目录
O、前言
一、进程程序替换的直观现象#xff08;什么是进程程序替换#xff1f;#xff09;
二、进程程序替换的原理
三、进程程序替换的函数#xff08… 标题[Linux] Linux 进程程序替换 个人主页水墨不写bug 图片来源于网络 目录
O、前言
一、进程程序替换的直观现象什么是进程程序替换
二、进程程序替换的原理
三、进程程序替换的函数怎么使用进程程序替换
1.execl
2.execlp
3.execle
1.execv
2.execvp
3.execve
四、进程程序替换的实际应用场景程序替换的意义
进程替换的常见场景 正文开始
O、前言 在之前我们已经学习了什么是进程的创建退出等待以及有关进程地址空间的问题。接下来的进程程序替换依然是属于进程控制的范畴但是在实际应用中是非常重要的一种技术。 本文不直接像书本上一样一开始就说一大堆概念让人摸不清头脑而是现观察进程程序替换的现象然后再从现象中在进一步了解进程程序替换。 一、进程程序替换的直观现象什么是进程程序替换 进程程序替换有一些专门对口的函数我们可以通过man手册查到其中的一个函数 接下来我们直接看一段代码用一用这个函数
#includestdio.h
#includeunistd.hint main()
{printf(process begin...\n);execl(/usr/bin/ls,ls,-a,-l,NULL);printf(process end...\n);return 0;
} 两个打印语句分别标识进程开始与结束话不多说直接开始运行结果如下 我们惊奇的发现执行的现象与使用ls的现象差不多 通过进一步观察可以发现 1在调用execl函数之前的代码打印process begin 的语句执行了 2调用execl函数就好像使用了 “ls” 命令 3调用execl函数之后的代码没有被执行。 为什么会发生这样的情况呢别急这就需要深入了解调用execl函数这一动作背后到底发生了什么——于是我们需要谈一谈进程替换的原理。
二、进程程序替换的原理 我们知道我们写好并编译好的文件是一个 独立的 可执行程序 包括我们自己写的mytest或者是系统上已经装好的ls等指令都是一个独立的可执行程序。 execl 这类函数的功能就是把一个新的独立的可执行程序覆盖式的加载到原来的运行起来的进程中从而实现程序替换。 一个进程包括内核数据结构 代码和数据这里的程序替换替换的不仅仅是数据还要替换代码这也就意味着原来的代码和数据就被覆盖了于是进程会从执行execl函数这一行开始直接执行新加载的代码和数据。原来的数据自然就丢失了这也就解释了 执行 execl 就像执行力 ls指令 在最后执行结束之后并没有打印 “process end” 的原因 1执行execl之后ls的代码和数据被加载的内存中覆盖替换了原来的代码和数据 2原来的 打印 “process end” 的代码由于被覆盖而丢失所以没有执行 3最终的返回值是ls指令的返回值而不再是原来被覆盖的进程的返回值。 三、进程程序替换的函数怎么使用进程程序替换 我们可以通过进程程序替换的函数名称来略知一二但是在那样通过函数名称来快速记忆之前我们还是需要先一个一个了解进程程序替换的函数们
list初始化列类型:
1.execl 就像我们之前演示的那样其实就是execl函数使用方法接下来需要对这个函数的传参细节做一些深入理解 参数列表 *pathname: 需要替换的可执行程序的位置需要指明具体的位置既可以使用绝对路径也可以使用相对路径。 比如想要替换ls命令ls命令本质是一个可执行程序位于/usr/bin/ls于是我们第一个参数需要这样传递“/usr/bin/ls” *arg: 需要替换的可执行程序的名称 需要替换的ls命令的名称就是ls于是需要传递“ls” ... : 参数列表 类似于printf的参数列表不限制打印的参数的个数一样我们在命令行上想要使用不同的命令传递的参数的个数是不同的于是通过参数列表我们可以传递不同的参数个数来达到正确执行不同指令的目的 比如下面的这一个实例就很好的体现了上述的规则 实例一 makefile: 当我们想要一次生成多个目标文件那么可以定义一个伪目标all 在伪目标后面 “ : ” “ 需要的依赖文件名称 ” 在下文表明生成依赖文件的依赖方法即可 .PHONY:all
all:mytest mmtestmmtest:mytest.ccg -o $ $^
mytest:pra_exec.cgcc -o $ $^.PHONY:clean
clean:rm -r mytest mmtest pra_exec.c: #includestdio.h
#includeunistd.hint main()
{printf(process begin...\n);execl(/usr/bin/ls,ls,-a,-l,NULL);printf(process end...\n);return 0;
} mytest.cc #includeiostream
#includeunistd.h
using namespace std;int main()
{coutC进程开始运行endl;execl(./mytest,./mytest,NULL);coutC进程结束运行endl;return 0;
} 由于我们已经编写了makefile所以只需要make就可以生成两个可执行程序。 运行结果 在进程运行的过程中我们发现进行了两次进程程序替换 ./mmytest C程序 --- ./mytestC程序 --- /usr/bin/ls 系统命令C程序 我们可以通过观察打印信息来观察。 到这里我们可以得出结论 execl可以替换任何语言的可执行程序。包括javapythonshell脚本语言等唯一不同的是通过execl调用的时候传递的参数不同。 接下来的介绍只给出具体函数的使用实例不再重复介绍同样性质的参数。
2.execlp
#include unistd.h
int main()
{// 带p的可以使用环境变量PATH无需写全路径execlp(ps, ps, -ef, NULL);exit(0);
}
3.execle
#include unistd.h
int main()
{char *const envp[] {PATH/bin:/usr/bin, TERMconsole, NULL};// 带e的需要自己组装环境变量execle(ps, ps, -ef, NULL, envp);exit(0);
}
vector数组类型
1.execv
#include unistd.h
int main()
{char *const argv[] {ps, -ef, NULL};//带v的需要传入数组形式的参数列表 execv(/bin/ps, argv);exit(0);
}
2.execvp
#include unistd.h
int main()
{char *const argv[] {ps, -ef, NULL};// 带p的可以使用环境变量PATH无需写全路径execvp(ps, argv);exit(0);
}
3.execve
#include unistd.h
int main()
{char *const argv[] {ps, -ef, NULL};char *const envp[] {PATH/bin:/usr/bin, TERMconsole, NULL};// 带e的需要自己组装环境变量execve(/bin/ps, argv, envp);exit(0);
} 函数解释 这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。 如果调用出错则返回-1 所以exec函数只有出错的返回值而没有成功的返回值。 命名理解 这些函数原型看起来很容易混,但只要掌握了规律就很好记。 l(list) : 表示参数采用列表 v(vector) : 参数用数组 p(path) : 有p自动搜索环境变量PATH e(env) : 表示自己维护环境变量 exec*函数簇函数之间的关系 本质上这些exec*函数都是对execve函数的封装为什么 因为execve是一个系统调用函数本质上底层调用的都是相同的形式调用的execve只不过是上层的封装转换的参数传递的方式。 四、进程程序替换的实际应用场景程序替换的意义 在我们的项目中我们的父进程一直是运行的不能说父进程执行了想要执行ls命令在执行了之后自己却退出了。所以我们在一般情况下一定不能让父进程发生程序替换不然父进程的代码和数据被子进程替换之后代码和数据就丢失了 所以我们需要fork创建子进程让子进程来代替父进程来执行exec*这一类的函数让子进程来发生程序替换让子进程的代码和数据被替换掉这样父进程就可以保留 进程替换的常见场景 用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序。 当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。 调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。 完~ 未经作者同意禁止转载