目录
直接看现象(单进程)
单进程程序替换原理
替换函数
直接看现象(单进程)
- 我们先使用execl来直接看现象man 3 execl
- execute a file执行一个程序
- int execl(const char *path, const char *arg, ...);
- execl函数的返回值:只要是替换成功,就不会向后继续运行;只要继续运行了,一定是替换失败了!(无序关心返回值)
- 请看下面代码☞☞
- ❗./testexec执行的是我们自己的程序,但是运行了一部分我们的程序,另外一部分是OS中ls指令的程序。
- ls也是用C语言写的可执行的二进制文件/程序(系统中独立存在的文件)
- file /usr/bin/ls
- 所以execl的作用:进程用exec*函数,执行起来新的程序。(把自己对应的代码和数据替换掉,执行新的程序)
1: testexec.c
1 #include<stdio.h>
2 #include<unistd.h>
3 int main()
4 {
5 printf("testexec.... begin!\n");
6 execl("/usr/bin/ls","ls","-l","-a",NULL);
7 printf("testexec... end!\n");
8 return 0;
9 }
【替换失败测试】
单进程程序替换原理
解释上面代码:
- 自己写的代码编译成一个可执行程序(在磁盘中)。
- 程序运行起来./。
- OS为当前的进程创建PCB/进程地址空间/页表等内核数据结构以及隐射关系。
- 数据和代码加载到内存中。
- 进程在运行过程中遇到execl,进程进行了程序替换。
- 进程 = 内核数据结构 + 代码 + 数据
- 程序替换后:内核数据结构:结构几乎不会改变部分属性会改变
- 程序替换后:主要是数据和代码:全部被新的程序的数据和代码覆盖掉
- 程序替换的本质:将当前的进程的代码数据进行替换的技术。
- 总之,创建进程:先创建内核数据结构,再创建数据/代码,建立隐射关系。(调用exec*系列函数去加载程序的代码和数据)
- 加载的过程就是把数据/代码从磁盘拷贝到内存,从一个硬件到另外一个硬件,必须有OS来参与,OS是内存的管理者。用户用的是execl加载。我们可以断定:exec*系列函数系统调用函数或者底层包含了系统调用函数。
❓有没有创建新的进程
- 没有创建新的进程。
- 内核数据结构基本不变,只有个别属性发生变化。
- 执行时一个新的进程用老进程的壳子,执行新程序的代码。
- 站在被替换的进程的角度:本质就是程序的代码和数据被加载到内存了!
❓程序运行之前必须先加载到内存
- 冯诺依曼体系结构规定CPU只能访问内存,不能访问外设。
❓可执行程序的二进制文件怎么加载到内存的
- exec*类似于一种Linux上的加载函数(加载器底层接口使用的)
❓程序替换的时候需要把旧程序的代码数据先清理掉,还是直接覆盖。
- 在程序替换或更新时,是否先清理旧程序的代码数据还是直接覆盖,这取决于你的具体需求和上下文。取决于具体实现。
替换函数
- OS中默认进程程序替换函数有6个库函数和1个系统调用函数。
- man 3 execl
- man 2 execve
- ... 表示可变参数:可变参数,也称为参数个数可变,是指在一个函数或方法定义时,其参数列表中可以包含任意数量的参数。这意味着,函数的参数个数是可变的,但参数类型通常是确定的或有所限制。(类似printf)(关于可变参数的实现在深度解剖C语言会讲到)
🎇单进程的进程程序替换会把我们原先的代码和数据全部覆盖掉。以上是单进程,我们想要将进程替换的同时不影响旧的进程。下篇我们将单进程替换代码改成多进程版❗