L20 内存使用与分段

:::info 管理内存的原因:

  • 计算机取指执行,指令在内存中
  • 使用绝对地址不方便,因为程序载入时,并不确定会放在哪里
  • 因此决定使用逻辑地址的机制:段地址+偏移地址,将程序中的地址看作逻辑地址
  • 但是在多进程的情况下,段地址也会经常发生改变,用什么机制来克服? :::

    重定位

    💎Part 07 内存管理 - 图1
    用逻辑地址来替代绝对地址
    是么时候完成重定位?

  • 编译时重定位的程序只能放在内存固定位置

  • 载入时重定位的程序一旦载入内存就不能动了
  • 应该是运行时完成重定位

why
💎Part 07 内存管理 - 图2 :::info 多进程应用仍然需要频繁的交换! ::: 💎Part 07 内存管理 - 图3
每个进程有各自的基地址,放在PCB中

  • 执行指令时第一步先从PCB中取出这个基地址
  • 切换进程时,因为PCB也会切换,所以自动将基地址切换了

    程序分段

    :::info why 程序分段
    因为不同的程序段有不同的特性:代码段只读、变量段大小固定、堆栈区会动态变化……
    分段进行管理内存,更加高效! ::: 💎Part 07 内存管理 - 图4
    如上图所示,将对应的代码段分区域放入内存

  • 这里就引出了LDT表

💎Part 07 内存管理 - 图5
LDT表存着不同的段对应的基址,通过查找LDT表可以获得想要的段的某个偏移地址下的内容。 :::tips 实际使用时是将GDT和LDT结合使用的
💎Part 07 内存管理 - 图6 ::: GDT:操作系统对应的段表,存进程的
LDT:每个进程对应的段表,存不同代码段的基地址。

L21 内存分区与分页

:::info 新的问题:如何分配空闲的内存

  • 固定分区
  • 可变分区 :::

    可变分区

    💎Part 07 内存管理 - 图7
    可以通过建立分区表的机制来控制 :::info 但是仍有问题

  • 存在内存碎片

  • 一旦碎片过多需要内存紧缩(重新把内容集中移动,这样耗时很大) ::: :::tips 解决方式:从连续到离散!—内存分页 ::: 💎Part 07 内存管理 - 图8
    例如在Linux0.11中,4k分成一页(正好3位);

  • 对所需要的空间,计算其大小并向上取整;

  • 这样最多浪费的空间不过4k
  • 上图中页仍是相对or逻辑的页,而页框是实际物理上分的页

💎Part 07 内存管理 - 图9 :::tips 页表中存了页号(可以理解为逻辑地址)和页框号的对应;
逻辑地址是由页号和偏移量组成,先根据页号找到实际的页框,然后根据偏移量得到实际的地址; :::

L22 多级页表与快表

:::info 问题:页太小了页表就变大了 :::

  • 为了避免空间浪费页应该尽可能小
  • 但是这样页表会变得非常大

2^20个页表项都得放在内存中,需要4M内存;系统中并发10个进程,就需要 40M内存

方法1:只放用到的页表

实际上大部分的逻辑地址根本用不到
image.png
不连续怎么从逻辑页号到物理页号?

  • 需要依次比较查找,需要耗费很多时间
  • 即使是使用二分查找也有影响

    方法2:多级页表

    一个页目录能控制4M的内存,此处不需要的页目录不会留存!
    💎Part 07 内存管理 - 图11 :::tips 牺牲是什么?

  • 多增加一级会增加访问内存次数! :::

    快表(TLB)

    TLB是一组相关联的快速存储,是寄存器 :::tips 存近期访问的页号! ::: 💎Part 07 内存管理 - 图12 :::info 通过硬件比对,一次查找即可得到的!

  • 若未查到,则仍是使用多级页表查找,并将查到的内容放到快表中。 ::: TLB命中时效率会很高,未命中时效率降低
    要想真正实现“近似访存1次” ,TLB的命中率应该很高 TLB越大越好,但TLB很贵,通常只有[64,1024] :::info whyTLB可以相对较小【64,1024】?

  • 程序的访问具有局部性

  • 程序多体现为循环和顺序结构。 :::