进程的程序替换
fork 创建的子进程,和父进程式共用一个代码块的,而事实上我们更需要创建出的子进程能够单独执行一份代码。
程序替换不会创建新进程,也不会销毁进程。
替换原理:
pcb进程不变,从虚拟地址空间通过页表映射到物理地址空间,然后从磁盘的其他可执行文件,替换代码段和数据段。原有的堆,栈中的数据都不要了,根据新的代码执行过程重新构建堆,栈的内容。(类似双击exe执行一个程序的过程(操作系统的记加载器模块))。
替换函数
有六种以exec开头的函数,统称exec函数。(参数有差异,底层原理完全相同)
#include <unistd.h>
l => list 变长参数列表 v => vector 数组
p => path,自动从path目录中找可执行目录,类似我们直接敲命令就可用 e =>
- int execl(const char *path, const char *arg, ...);
execl("/usr/bin/ls", "/usr/bin/ls", "/", NULL); //含义:ls / (ls 根目录)
最后一个参数必须是NULL,如果不填程序就是未定义行为。
- int execlp(const char *file, const char *arg, ...);
execlp("ls", "ls", "/", NULL); //含义:ls / (ls 根目录) //第一个不用写路径,直接写文件名,自动从path中寻找 //第二个 ls 替换 第一个 ls
- int execle(const char *path, const char *arg, ...,char *const envp[]); 用户进行程序替换的时候,可以手动指定环境变量
char* env[] = { "AAA=BBB", NULL, //数组必须以NULl结尾 }; execle("./aaa", "./aaa", NULL, env); //含义:ls / //./aaa 是aaa.c文件gcc生成的
- int execv(const char *path, char *const argv[]);
char* env[] = { "/usr/bin/ls", "-1", "/", NULL, //数组必须以NULl结尾 }; execv("/usr/bin/ls", argv); //含义:ls -l / //argv 替换 /usr/bin/ls 的内容
- int execvp(const char *file, char *const argv[]); 和 execlp 类似
- int execve(const char *path, char *const argv[], char *const envp[]); 和execle类似,手动设置环境变量
参数中:path:路径 arg:命令行参数 fie:文件名
返回值:
这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。
如果调用出错则返回-1,所以exec函数只有出错的返回值而没有成功的返回值。
程序替换经常搭配fork来使用,一旦替换,就会把原来的代码和数据都干掉,无法继续执行原来的代码。
#include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<sys/wait.h> int main() { pid_t ret1 = fork(); if (ret1 == 0) { //child printf("before execl\n"); execl("/usr/bin/ls", "/usr/bin/ls", "/", NULL); //含义:ls / execlp("ls", "ls", "/", NULL); //含义:ls / char* env[] = { "AAA=BBB", NULL, //数组必须以NULl结尾 }; execle("./aaa", "./aaa", NULL, env); //含义:ls / //./aaa 是aaa.c文件gcc生成的 //结果是 BBB char* env[] = { "/usr/bin/ls", "-1", "/", NULL, //数组必须以NULl结尾 }; execv("/usr/bin/ls", argv); //含义:ls -l / } //father wait(NULL); printf("after execl\n"); system("pause"); return 0; } //aaa.c 文件中的内容 #include<stdio.h> #include<stdlib.h> int main() { printf("%s\n", getenv("AAA")); //加入环境变量AAA 但是会返回空指针,就会吐核错误 return 0; }