目录
进程替换:
execl函数解析:
多进程替换的本质:
exec系列函数解析:
进程替换不会替换原进程的环境变量:
进程替换:
-
通过 fork 创建的进程,在最初会执行父进程代码的一部分,这通常包括 fork 之后的代码分支。父进程和子进程在 fork 调用后会继续执行相同的代码,但它们在不同的进程上下文中独立运行。
-
如果希望 fork 创建的子进程执行与父进程完全不同的代码并处理新的数据,从而不再与父进程有任何关联,则需要进行进程程序替换。这通常是通过 exec 系列函数实现的。
-
进程程序替换的目的,就是将子进程的当前进程映像替换为一个新的程序映像,使得子进程能够执行与父进程无关的代码,并处理与父进程不同的数据。这一操作使得子进程脱离了父进程的上下文,完全专注于新的任务。
-
通过进程程序替换,子进程不再延续父进程的执行逻辑,而是加载并运行一个全新的程序。这样,父进程和子进程在逻辑上可以完全独立,适用于需要子进程承担不同任务的场景。
execl函数解析:
int ececl(const char* path,const char * arg, ...);
//const char* path : 一个字符串,表示要执行的程序的路径。可以是绝对路径或相对路径。
//const char * arg : 新程序的名称
// ... 代表可变参数列表,传递给程序的命令行参数。
//要注意写完参数后,结尾要加上一个NULL。
// 调用 execl 函数执行 /bin/ls 程序
execl("/bin/ls", "ls", "-l", "-a", NULL);
-
如果函数调用成功,没有返回值。
-
如果函数调用失败,返回-1。
多进程替换的本质:
-
在多进程环境中,当一个子进程发生进程替换时,该子进程的进程控制块(PCB)、地址空间、页表都会被替换为新的进程的对应内容。进程替换通过加载新程序的代码和数据,使得子进程的执行环境完全变成新进程的环境。
-
进程替换并不会创建新的子进程,它只是将现有的子进程替换为其他进程。因此,子进程在执行新程序时不再延续父进程的代码和数据,但父子进程之间的关系依然保持不变,进程替换后的子进程仍然是父进程的后代。
-
通过进程替换,子进程要执行的代码和父进程的代码完全解耦。这种解耦确保了子进程不会覆盖父进程的代码,而是独立运行新程序,避免了对父进程的任何干扰或影响。
-
如果进程替换成功,原来的子进程在 exec 系列函数之后的语句将不会再被执行,因为子进程的执行环境已被完全替换为新程序。exec 之后的原代码段将被新程序的代码覆盖,根本没有机会再被执行。如果 exec 调用失败,则会返回错误,子进程会继续执行 exec 之后的原代码,这意味着进程替换没有成功。
exec系列函数解析:
execl("/bin/ls", "ls", "-l", "-a", NULL);
execlp("ls", "ls", "-l", "-a", NULL);//不用传程序所在目录,会自己去环境变量path遍历查找。
char* const argv[]={"ls", "-l", "-a" , NULL};
execv("/bin/ls", argv);
char* const argv[]={"ls", "-l", "-a" , NULL};
execvp("ls",argv); //也可以使用下面的写法
execvp(argv[0],argv);
-
以上接口,都是对execve系统调用的封装,只有execve是系统接口。
进程替换不会替换原进程的环境变量:
-
子进程会继承父进程的环境变量,这一过程是通过子进程继承父进程的地址空间实现的。环境变量在地址空间中作为一部分数据,被子进程完整复制。
-
在进程替换之后,虽然子进程的代码和数据被新程序替换,但环境变量不会自动被替换。新进程会继续使用原子进程从父进程那里继承的环境变量表。这意味着替换后的进程仍然保持着原来的环境变量设置。
-
如果在进程替换时使用 execle,并传递了自定义的环境变量表,那么这个自定义的环境变量表将覆盖原子进程继承自父进程的环境变量表。也就是说,新进程将使用传递给 execle 的环境变量,而不是原来的环境变量。
-
如果希望同时保留原来继承的环境变量,并添加一些自定义的环境变量,可以使用 putenv 函数在父进程中添加自定义的环境变量。这样,子进程在执行进程替换之前就会继承这些新的环境变量,替换后的进程也能够继续使用这些附加的环境变量。
int main()
{
char* const myenv[]={"MYVAL1=1111","MYVAL2=1111","MYVAL3=1111"}; //自定义环境变量表
char* env_val="MYVAL4=11111";//创建一个环节变量
putenv(env_val);//添加到父进程的环境变量表
pid_t id=fork();
if(id==0) // 子进程
{
execle("/bin/ls", "ls","-l", "-a",NULL, myenv); //使用自己的环境变量表覆盖原环境变量表
}
}