前言
计算机的工作原理,取指执行,那就意味着程序必须放在内存中才能执行,我们编写的C文件,放在磁盘上,然后编译成可执行程序,最终执行。可执行程序的执行过程中要分内存,然后取指执行
一、内存使用与分段
1,内存使用
重定位最何时的时机–运行时重定位
- 编译成可执行程序时,会有一个地址,是逻辑地址,例如40, 300
- 运行指令时,PCB中存放的有基地址,然后就可以算出实际要寻找的地址,目前理解为物理地址,但学到后面,其实也知道这个不是物理地址,是虚拟地址
- 首先程序编译完成,执行:
- 创建进程1和进程2,各找到一段空闲内存,并把各地址作为 base 赋给各进程的PCB
- 程序载入到 base 为基址的内存
2,程序分段
程序可分为数据段和代码段,还有堆栈段
一般PCB 会存放LDT(局部描述符表)。这是结构体数组,里面放着该进程的代码段和数据段的基地址
二、内存分区和分页
引入分区:内存分区导致的内存效率问题
将内存分割固定大小的页,一般页大小为 4K ,程序请求内存按页分配,
- 0x2240 % 0x1000(4K) == 0x2240 >> 12 ,得到页号
- 逻辑地址 :0x02 : 0x240 ,查页表,页表指针在PCB中
- 由页号查到页框号为3,物理地址为0x3240
三、多级页表与快表
物理内存按照4K 分页后,会需要页表存储页和页框的关系,分页小,会导致页表太大,故提出多级页表
- 页目录4K 一个页目录存放1024个页表指针,一个页表存放1024个页指针,一个指针4字节,1024 *1024 *4(byte) = 4M,可以看出一个页目录要是已全部填充的话需要4M
- 如图,假如一个程序,使用一个页目录其中的三个成员,按照上面的算法,需要使用4K, 但按照多级页表,只需要一个页目录表和三个页表 一共16K ,故省去了很多内存空间,多级内存页表的好处
多级页表存在多次访问的问题,影响效率,所以又提出缓存的概念,TLB
TLB是一组相联快速存储, 是寄存器。
这个缓存能成立的原因是程序的访问具有局部性
四、段页结合的实际内存管理
段、 页结合: 程序员希望用段,物理内存希望用页。
实际的段页结合的内存管理
-
分配段、 建段表; 分配页、 建页表
-
进程带动内存使用的图谱
-
从进程fork中的内存分配开始
-
0x300是逻辑地址,根据LDT 找到基地址0x45000,
-
得到虚拟地址0x00045300,
-根据逻辑地址算出是第一个页号 0x45, -
根据页表查到页框号,得到实际的物理地址
1.故事从fork()开始,分配续集内存,建段表
注意nr ,个人理解是第几个进程,每个进程从虚拟内存里分配64M,p 就是pcb(进程控制块),数据段和代码段公用一个段
父进程已经分配好内存,目前子进程就不要分配内存了,和父进程共用一套
from 就是代码段的虚拟地址,32虚拟地址代表意义如下:
- 0.11版本一个进程需要64M,从虚拟地址里按照64M切块,每个进程对一个64M
- 一个页目录表4K,一个页目录项4字节,故有1024个页目录项
- 一个页目录项指向一个4K 大小的页表,一个页表可以管理4M
- 一个页目录项可以管理4M,一个进程需要6个页目录项
- 一个页目录表可以管理64个进程
fork()之后,父子进程共用页框
2,父子进程的操作
- 进程1 是 进程2 的父进程,在虚拟内存上分配的不同,进程1占用64M—128M,进程2占用128M—192M
- 当执行 *p = 7时,首先 p 为 0x300 ,接着通过查 LDT 中的段表得到虚拟地址 0x00400300,虚拟地址再通过页表找到物理内存,写入内存,设置为只读
- 当执行 *p = 8时,首先 p 为 0x300 ,接着通过查 LDT 中的段表得到虚拟地址 0x00800300,虚拟地址再通过页表找到物理内存,查看到为只读,所以新申请内存页(写时拷贝)
参考链接:
1:https://blog.csdn.net/qq_43118676/article/details/106339207