内存管理
虚拟内存
操作系统引入了虚拟内存的概念
- 程序所使用的内存地址叫做虚拟内存地址(Virtual Memory Address)
- 实际存在硬件里面的空间地址叫物理内存地址(Physical Memory Address)。
内存分段 Segmentation
driven: 用户执行的程序(虚拟内存中),在物理内存中找一块地方放(段的大小不固定)
虚拟地址通过段表与物理地址进行映射。
虚拟地址切分为:段选择因子和段内偏移量:
- 段选择子就保存在段寄存器里面。段选择子里面最重要的是段号,用作段表的索引。段表里面保存的是这个段的基地址、段的界限和特权等级等。
- 虚拟地址中的段内偏移量应该位于 0 和段界限之间,如果段内偏移量是合法的,就将段基地址加上段内偏移量得到物理内存地址。
分段会产生外部内存碎片
解决外部内存碎片:内存交换 —— 先把内存碎片中间的东西交换到硬盘上,再从硬盘上拿回到内存(放在两侧,使外部内存碎片得以合并)
但是!这样效率比较低,因为硬盘访问速度太慢了!
内存分页 Paging
driven:把虚拟内存和物理内存切分成很多个固定大小的页,比如一片4kb
分页不会产生外部碎片,因为它可以将连续的虚拟地址映射为不连续的物理地址。
但是分页可能产生内部碎片。例如当程序不足一页大小时,还是会给它分配一页。
当进程访问的虚拟地址在页表中查不到时,系统会产生一个缺页异常,进入系统内核空间分配物理内存、更新进程页表,最后再返回用户空间,恢复进程的运行。
如果内存空间不够,操作系统会把其他正在运行的进程中的「最近没被使用」的内存页面给释放掉,也就是暂时写在硬盘上,称为换出(Swap Out)。一旦需要的时候,再加载进来,称为换入(Swap In)。所以,一次性写入磁盘的也只有少数的一个页或者几个页,不会花太多时间,内存交换的效率就相对比较高。
分页的方式使得我们在加载程序的时候,不再需要一次性都把程序加载到物理内存中。我们完全可以在进行虚拟内存和物理内存的页之间的映射之后,并不真的把页加载到物理内存里,而是只有在程序运行中,需要用到对应虚拟内存页里面的指令和数据时,再加载到物理内存里面去。
虚拟地址切分为:页表和页内偏移量
多级页表
如果页表只有一级,由于页表项比段表项更多,那么占用内存会更多。每个进程都有自己的虚拟地址空间,也就是说都有自己的页表,使差距继续几何倍数增长。
可以用多级页表来解决。由于局部性原理,如何某个n级页表没有被用到,你那么便无需创建其的n+1级页表。
64位系统一般由四级目录:
- 全局页目录项 PGD(Page Global Directory);
- 上层页目录项 PUD(Page Upper Directory);
- 中间页目录项 PMD(Page Middle Directory);
- 页表项 PTE(Page Table Entry);
TLB
在 CPU 芯片中,加入了一个专门存放程序最常访问的页表项的 Cache,这个 Cache 就是 TLB(Translation Lookaside Buffer) ,通常称为页表缓存、转址旁路缓存、快表等。
在 CPU 芯片里面,封装了内存管理单元(Memory Management Unit)芯片,它用来完成地址转换和 TLB 的访问与交互。
有了 TLB 后,那么 CPU 在寻址时,会先查 TLB,如果没找到,才会继续查常规的页表。
TLB 的命中率很高,因为程序最常访问的页就那么几个。
段页式
将分段和分页结合。
段页式内存管理实现的方式:
- 先将程序划分为多个有逻辑意义的段,也就是前面提到的分段机制;
- 接着再把每个段划分为多个页,也就是对分段划分出来的连续空间,再划分固定大小的页;
地址结构由段号、段内页号和页内位移三部分组成。