1. 进程替换的概念
进程替换是指在一个正在运行的进程中,用一个新的程序替换当进步程的代码和数据,使得进程开始实行新的程序,而不是原来的程序。
这种技术通常用于在不创建新进程的情况下,改变进程的行为。
我们之前谈到过fork函数,这个函数可以启动一个子进程,子进程继承了父进程的代码和数据。
在谈到进程替换之前,我们只能通过判断fork函数的返回值id来区分父子进程,并让二者运行不同的分支。而利用进程替换技术,我们可以将子进程的代码数据完全替换为另一个程序,实现我们所盼望的,父子进程完全独立为两个不同的进程。
进程替换的原理
进程替换的原理涉及到操作体系的内存管理和进程控制。当一个进程调用exec系列函数时,操作体系会将新程序的代码和数据加载到内存中,并将其与当进步程的地址空间相关联。这个过程通常涉及到以下几个步调:
- 加载新程序:操作体系将新程序的可实行文件从磁盘加载到内存中。
- 替换代码和数据:新程序的代码和数据会替换当进步程的代码和数据段。
- 更新进程状态:进程的状态会被更新,以反映新程序的实行状态。
- 实行新程序:进程开始实行新程序的入口点,通常是main函数。
在这个过程中,进程的标识符(PID)和其他一些属性(如打开的文件描述符、环境变量等)通常会保持不变。
2. exec进程替换函数
在Linux体系中,进程替换通常通过exec系列函数来实现,该系列函数包罗在头文件<unistd.h>。
这些函数包罗:
- execl:实行一个新程序,参数以列表形式给出。
- int execl(const char *pathname, const char *arg, ...);
复制代码 - execlp:实行一个新程序,参数以列表形式给出,并在环境变量PATH中搜索程序。
- int execlp(const char *file, const char *arg, ...);
复制代码 - execle:实行一个新程序,参数以列表形式给出,并提供自界说的环境变量。
- int execle(const char *pathname, const char *arg, ...);
复制代码 - execv:实行一个新程序,参数以数组形式给出。
- int execv(const char *pathname, char *const argv[]);
复制代码 - execvp:实行一个新程序,参数以数组形式给出,并在环境变量PATH中搜索程序。
- int execvp(const char *file, char *const argv[]);
复制代码 - execve:实行一个新程序,参数以数组形式给出,并提供自界说的环境变量。
- int execve(const char *pathname, char *const argv[], char *const envp[]);
复制代码 - execvpe:实行一个新程序,参数以数组形式给出,并提供自界说的环境变量。
- int execvpe(const char *file, char *const argv[], char *const envp[]);
复制代码 这些函数的使用方式和参数传递方式略有不同,但它们的基本功能都是相同的:用新程序替换当进步程的代码和数据。
记忆本事:
- l(list):表现参数接纳列表。
- v(vector):参数用数组。
- p(path):到环境变量PATH中搜索指定程序,无需完整路径(带p的函数第一个参数为file,代表可实行程序;不带p的函数第一个参数为pathname,代表完整路径)。
- e(env) : 表现自界说环境变量,不带e的表现继承当前的环境变量。
使用示例:
- #include <stdio.h>
- #include <unistd.h>
- int main()
- {
- char* vector[] = {"ls", "-l", "-a", NULL};
- int id = fork();
- if(id == 0)
- {
- execvp("ls", vector);
- return 0;
- }
- int pid = wait(NULL);
- return 0;
- }
复制代码 注意:传入的参数为命令行参数,也就是说在命令行要实行该程序需要输入什么,参数就传递什么,主要是不要忘记选项是从第二个参数开始的。
第一个参数传什么都不要紧,随你喜欢,但要记得传:
- #include <stdio.h>
- #include <unistd.h>
- int main()
- {
- char* vector[] = {"cxk", "-l", "-a", NULL};
- int id = fork();
- if(id == 0)
- {
- execvp("ls", vector);
- return 0;
- }
- int pid = wait(NULL);
- return 0;
- }
复制代码 execve函数
该函数相比于其他函数具有一定的特殊性,他是上述函数中唯一一个体系调用。
在命令行输入[man exec]能查到如下信息,可以看到并没有execve的存在,且这些函数都在3号手册当中:
只有单独查询execve函数时才气查到,可以看到该函数在2号手册(体系调用接口)中:
execve函数的特殊性:
- 体系调用层级的基础地位
- execve在exec函数族中具有特殊的基础性地位。它是直接与体系调用接口紧密相连的函数。其他的exec系列函数(如execl、execv等)在很多情况下终极可能会调用execve来实现实际的进程替换操作。
- 例如,在一些库函数的实现中,为了提供更方便的参数传递方式(如execl的可变参数列表形式),可能会在内部对参数进行处置惩罚后调用execve来完成进程替换的核心功能。
- 参数处置惩罚方式的不同
- execve的参数包罗要实行的程序文件路径、传递给新程序的命令行参数数组以及环境变量数组。这种参数形式与其他exec函数有所不同。
- 像execl函数,它的参数是以可变参数列表的形式,末了以NULL末端,这种形式在使用上有一定的便利性,但在底层实现中可能需要更多的转换工作才气与体系调用接口对接,而execve的参数形式更直接地反映了体系调用的需求。
- 安全和权限方面的思量
- 由于execve是直接进行进程替换的底层函数,在安全和权限管理方面有偏紧张的作用。它对可实行文件的路径、实行权限等有着严格的要求。
- 当调用execve时,体系会根据文件的权限设置(如是否可实行、所属用户和组等)以及当进步程的权限来判断是否允许进程替换操作。这种严格的权限检查有助于保障体系的安全性。
- 与内核交互的特点
- execve在与内核交互时,需要将新程序的代码和数据加载到当进步程的地址空间,同时更新进程的各种状态信息,如程序计数器、堆栈指针等。这个过程涉及到内核中的进程管理、内存管理等多个模块的协同工作。
- 相比其他exec函数,execve在与内核的这种深度交互方面更为直接,因为其他函数可能会在调用execve之进步行一些额外的参数处置惩罚或环境设置。
exec函数族调用关系如下:
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |