目录
多进程程序替换
多进程程序替换原理
进程程序替换函数详解
execl&execv
execlp&execvp
execle&execvpe
execve
多进程程序替换
- 我们想要进程替换的同时不影响旧的进程(使用多进程版)
- fork创建子进程,让子进程去替换执行新程序(因为替换进程本身不会创建新的进程)
- 父进程依旧执行旧的进程,且等待子进程(非阻塞等待)
- 父进程只等待子进程(阻塞等待)
- 子进程可以替换系统指令,也可以替换我们自己的程序,只要是一个可执行程序都可以替换。
- 两全其美:既让子进程完成了任务,又不能让父进程受到影响
说明fork创建子进程完成任务:
- 让子进程执行父进程代码的一部分
- 让子进程执行一个全新的程序
- 基于进程间的独立性原则。
- fork创建的子进程默认在不修改的前提下,父子进程的数据和代码时共享的。
- 子进程发生了进程替换,相当于发生写入,发生了写时拷贝。
- 执行一个全新的程序,父子进程是独立的,所以不仅数据要写时拷贝,代码也要发生写时拷贝。只要程序替换成功了,父子进程无论是代码/数据在内核数据结构层面上,数据代码层面上彻底分开了。
子进程发生程序替换:
- 共享父进程的数据代码
- 暂停了把新程序的代码和数据从磁盘到内存的加载/写入
- 发生写时拷贝
- 开启加载/写入(页表的权限会改变)
- 重新建立映射关系
1: testexec.c
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<stdlib.h>
4 #include<sys/types.h>
5 #include<sys/wait.h>
6
7 int main()
8 {
9 printf("testexec.... begin!\n");
10 pid_t id = fork();
11 if(id == 0)
12 {
13 //child
14 sleep(2);
15 execl("/usr/bin/ls","ls","-l","-a",NULL);
16 exit(1);
17 }
18 //father
19 int status = 0;
20 pid_t rid = waitpid(id,&status,0);
21 if(rid > 0)
22 {
23 printf("father wait success!,child exit code: %d\n",WEXITSTATUS(status));
24 }
25 printf("testexec... end!\n");
26 return 0;
27 }
//测试失败
execl("/usr/bin/lsss","lsss","-l","-a",NULL);
【测试成功】细看退出码
【测试失败】细看退出码
多进程程序替换原理
- 用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支)
- 子进程往往要调用一种exec函数以执行另一个程序。
- 当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换。
- 从新程序的启动,程开始执行。
- 调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。
进程程序替换函数详解
前篇粗略的介绍了下替换函数,现在一个一个详解介绍。使用所有的替换方法,并且认识函数参数的含义
- OS中默认进程程序替换函数有6个库函数和1个系统调用函数。
- 其实有六种以exec开头的函数,统称exec函数
#include <unistd.h>`
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 execvpe(const char *file, char *const argv[],char *const envp[]);
//系统调用函数
int execve(const char *path, char *const argv[], char *const envp[]);
execl&execv
- l:list列表
- int execl(const char *path, const char *arg, ...);
- path:表示替换程序的路径(绝对/相对路径均可)☞☞怎样找到可执行程序需告知
- arg:可变参数,在命令行中怎么执行,就怎么传参。☞☞你想怎么执行
- arg形成命令行参数表传给ls指令的可执行程序main函数接收使用
- 注意❗最后必须以NULL结尾
V:Vector动态数组- int execv(const char *path, char *const argv[ ]);
- path:表示替换程序的路径(绝对/相对路径均可)☞☞怎样找到可执行程序需告知
- argv:指针数组(可变参数与其一个一个传参,直接放到数组里面,传数组即可)(带入命令行参数表)
- argv被当作命令行参数表传给ls指令的可执行程序main函数接收使用
- 字符串的类型是const char*变成char*需要强转
- 注意❗最后必须以NULL结尾
- 这两个函数只是在传参形式上发生了变化,实际上没有区别。
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<stdlib.h>
4 #include<sys/types.h>
5 #include<sys/wait.h>
6
7 int main()
8 {
9 printf("testexec.... begin!\n");
10 pid_t id = fork();
11 if(id == 0)
12 {
13 //child
14 sleep(2);
15 execl("/usr/bin/ls","ls","-l","-a",NULL); //❗
16 exit(1);
17 }
//
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<stdlib.h>
4 #include<sys/types.h>
5 #include<sys/wait.h>
6
7 int main()
8 {
9 printf("testexec.... begin!\n");
10 pid_t id = fork();
11 if(id == 0)
12 {
13 //child
14 sleep(2);
15 char *const argv[] =
16 {
17 (char*)"ls",
18 (char*)"-l",
19 (char*)"-a",
20 (char*)"--color",
21 NULL
22 };
23 execv("/usr/bin/ls",argv); //❗
24 exit(1);
25 }
26 //father
27 int status = 0;
28 pid_t rid = waitpid(id,&status,0);
29 if(rid > 0)
30 {
31 printf("father wait success!,child exit code: %d\n",WEXITSTATUS(status));
32 }
33 printf("testexec... end!\n");
34 return 0;
35 }
execlp&execvp
- p:环境变量PATH
- 带p的函数不用带绝对/相对路径传递参数了,表示用户可以不传递替换程序的路径(但是要传递可执行程序的文件名file)
- 用户可以不传递要执行的文件路径(文件名要传),直接告诉exec*,我要执行谁即可。
- p:查找这个程序,系统会自动在环境变量PATH中进行查找。
- int execlp(const char *file, const char *arg, ...);
- int execvp(const char *file, char *const argv[ ]);
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<stdlib.h>
4 #include<sys/types.h>
5 #include<sys/wait.h>
6
7 int main()
8 {
9 printf("testexec.... begin!\n");
10 pid_t id = fork();
11 if(id == 0)
12 {
13 //child
14 sleep(2);
15 execlp("ls","ls","-l","-a",NULL); //❗
16 exit(1);
17 }
//
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<stdlib.h>
4 #include<sys/types.h>
5 #include<sys/wait.h>
6
7 int main()
8 {
9 printf("testexec.... begin!\n");
10 pid_t id = fork();
11 if(id == 0)
12 {
13 //child
14 sleep(2);
15 char *const argv[] =
16 {
17 (char*)"ls",
18 (char*)"-l",
19 (char*)"-a",
20 (char*)"--color",
21 NULL
22 };
23 execvp("ls",argv); //❗
24 exit(1);
25 }
26 //father
27 int status = 0;
28 pid_t rid = waitpid(id,&status,0);
29 if(rid > 0)
30 {
31 printf("father wait success!,child exit code: %d\n",WEXITSTATUS(status));
32 }
33 printf("testexec... end!\n");
34 return 0;
35 }
execle&execvpe
- e:environment环境变量
- int execle(const char *path, const char *arg, ...,char *const envp[ ]);
- int execvpe(const char *file, char *const argv[ ],char *const envp[ ]);
- bash有环境变量,也可以获取命令行参数
- bash创建父进程
- fork创建子进程
- 通过exec*等函数将环境变量表和命令行参数表交给可执行程序
- 子进程运行起来,并使用两张表
execve
🙂感谢大家的阅读,若有错误和不足,欢迎指正。 下篇收尾❗