一、理解进程程序替换如果我们想要在一个进程里面执行另一个程序那么就需要进行程序替换也就是父进程创建一个子进程然后通过程序替换的方式将想要执行的程序交给这个子进程去去执行。在这里插入图片描述用fok创建一个进程的子进程之后如果要用这个子进程去执行一个新的程序那么就要用到exec系列接口进行程序替换。创建一个子进程这个子进程会拷贝父进程的从而拥有自己的task_struct新建结构体并大部分拷贝父进程、虚拟地址空间和页表代码和数据与父进程共享。而一旦通过特殊接口进行程序替换这个子进程的task_struct还是那个task_struct但是虚拟地址空间会按照新的程序经由编译器分配好的虚拟地址来且所有的页表映射将重置。之后执行程序的时候按需将对应程序的代码和数据加载到物理内存里面去然后建立新的映射关系。所以调用exec系列函数前后pid是不会变的。为什么一定是父进程创建子进程 子进程程序替换的方式因为程序替换会直接重置页表虚拟地址用的又是新的程序的。如果直接由父进程进行程序替换的话父进程它本身原来要执行的程序就相当于直接销毁了。二、程序替换函数2.1 笼统介绍代码语言javascriptAI代码解释int execl(const char *path, const char *arg, ...); int execlp(const char *file, const char *arg, ...); int execle(const char *path, const char *arg, ...,char *const envp[]); int execv(const char *path, char *const argv[]); int execvp(const char *file, char *const argv[]); int execve(const char *path, char *const argv[], char *const envp[]);这是一系列/一套函数这些函数的作用都是进行程序替换的它们的前缀都是exec。如果程序替换成功那么会直接执行替换后的程序的代码不会有返回值如果执行失败了会返回-1。所以exec系列函数只有出错的返回值没有成功的返回值。它们的命名都很有特点接下来会分别介绍这些exec系列函数然后进行总结。2.2 execl在这里插入图片描述使用的时候要包含unistd.h头文件。这个函数具有两个参数其中第一个要传递的参数是目标程序的完整路径第二个参数是程序参数列表是一个可变参数其中传递的参数的个数是不确定的。const char* path需要传递一个完整的路径。相对路径和绝对路径都是可以的但必须完整。const char* arg, …这个参数列表要求第一个参数为该程序的名字而最后一个参数必须是NULL作为可变参数列表的结束标志。中间的为功能参数。我们在传递这一部分参数的时候命令行上面是怎么写的这里就怎么写。比如调用ls时命令行ls -a -l那么这里就写ls, “-a”, “-l”, NULL。为什么取名叫execll对应list。这是因为在传递第二个参数的时候传递的参数就跟list一样是逐个列出的。—参数以列表形式逐个传入。示例代码语言javascriptAI代码解释1 #includeunistd.h 2 #includestdio.h 3 #includesys/types.h 4 #includewait.h 5 6 int main() 7 { 8 pid_t id fork(); 9 if (id 0) 10 { 11 printf(我是一个子进程接下来我要进行程序切换了\n); 12 execl(/usr/bin/ls, ls, -a, -b, NULL); 13 } 14 printf(我是一个父进程我回收了子进程\n); 15 pid_t sid waitpid(id, NULL, 0); 16 return 0; 17 }在这里插入图片描述成功进行了程序的切换在这个程序里面执行了ls这个二进制程序。2.3 execv在这里插入图片描述使用的时候包含头文件unistd.h。这个函数同样具有两个参数第一个参数就不做解释了第二个参数是一个参数数组也就是需要传递一个参数数组这个数组里面的元素是char*类型的。char* const argv[]要使用这个函数就必须先创建一个参数数组同样这个参数数组的第一个元素必须是目标程序的程序名而最后一个元素必须是NULL。之后把这个参数数组传递给这个函数就好。为什么取名叫execvv对应vector。因为execv在传递第二个参数的时候与execl不同它传递的是一个参数数组。—参数以数组形式传入。示例代码语言javascriptAI代码解释7 int main() 8 { 9 pid_t id fork(); 10 if (id 0) 11 { 12 printf(我是一个子进程pid%d我要进行程序切换了\n, getpid()); 13 char* argv[] {(char*)ls, 14 (char*)-a, 15 (char*)-l, 16 NULL}; 17 execv(/usr/bin/ls, argv); 18 exit(0); 19 } W 20 pid_t sid waitpid(id, NULL, 0); 21 printf(我是一个父进程我回收了子进程\n); 22 return 0; 23 }在这里插入图片描述不过execl和execv还有一点不同参数类型execl要求的是const char*直接传入字符串常量就行而execv要求的是char*在数组里面还要给字符串常量强转成char*。2.4 execlp和execvp在这里插入图片描述在这里插入图片描述execlp相比于execl多了个pexecvp相对于execv也是同样这个p就是PATH环境变量这两个函数的第一个参数是file也就是要我们传递所要执行文件的名字而不是一个路径了。传递所要执行文件的名字之后操作系统会根据这个名字去指定PATH环境变量下面存储的路径挨个去找。示例代码语言javascriptAI代码解释7 int main() 8 { 9 pid_t id fork(); 10 if (id 0) 11 { 12 printf(我是一个子进程pid%d我要进行程序切换了\n, getpid()); 13 char* argv[] {(char*)ls, 14 (char*)-a, 15 (char*)-l, 16 NULL}; 17 execvp(ls, argv); 18 exit(0); 19 } W 20 pid_t sid waitpid(id, NULL, 0); 21 printf(我是一个父进程我回收了子进程\n); 22 return 0; 23 }在这里插入图片描述2.5 execve、execle和execvpe在这里插入图片描述在这里插入图片描述这个e就是environment代表的是环境变量表带e的exec系列函数支持手动传递环境变量表即第三个参数。如果你给它传递一个新的环境变量表它就会用这个新的环境变量表如果你传的是environ那么它就还是用原来的这个环境变量表如果你不想让它用新的环境变量表而只是想在environ的基础之上添加一个环境变量给替换后的程序使用那就先对environ进行预处理可以用putenv函数添加环境变量。在这里插入图片描述示例execvpe执行自定义文件 myfiletest.c代码语言javascriptAI代码解释7 int main() 8 { 9 extern char** environ; 10 pid_t id fork(); 11 if (id 0) 12 { 13 printf(我是一个子进程pid%d我要进行程序切换了\n, getpid()); 14 char* argv[] {(char*)myfile, 15 NULL}; 16 putenv((char*)hahahelloworld); 17 putenv((char*)PATH/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/mosheng/.local/bin:/home/mosheng/bin:/home/mosheng /my_file); 18 execvpe(myfile, argv, environ); 19 exit(0); 20 } W 21 pid_t sid waitpid(id, NULL, 0); 22 printf(我是一个父进程我回收了子进程\n); 23 return 0; 24 }myfile.c代码语言javascriptAI代码解释3 int main() 4 { 5 printf(我是file文件程序切换成功啦\n); 6 printf(%s\n, getenv(haha)); 7 return 0; 8 }我在test.c文件里面用putenv给PATH环境变量添加了一个新的路径用来找到目标文件myfile然后又用putenv给环境变量表添加了一个全新的环境变量hahahelloworld。然后把预处理好的环境变量表传递过去execvpe最终执行成功。2.6 命名总结v和l只能有一个彼此互斥它们代表了参数的传入方式。l就是list参数以列表形式逐个传入。v就是vector参数以数组形式传入。p就是PATH环境变量第一个参数不再是绝对路径而是文件名操作系统会根据这个文件名去PATH环境变量里面存储的路径挨个去找。有p就不用传递路径前提是环境变量PATH有这个路径没p就一定要传递路径相对路径或者是绝对路径。程序替换替换为一个二进制可执行文件无论是用什么语言c、java、python、脚本语言编写的都是可以切换的毕竟这些语言编译链接了之后都是二进制可执行文件—跨语言。e就是environment是环境变量表。execve是系统提供的函数而其他的六个exec系列函数都是库函数都是在execve的基础之上进行实现的。