0%

L21-内存分区与分页

  • 前面已经说过,将程序存储在内存中分为三步:

    1. 在编译阶段将一个完整的程序分为好几段;
    2. 在内存中找到空闲的空间来存储这些程序段
    3. 将每个程序段存储在找到的空闲内存中,并将基地址存储在进程控制块 PCB 的 LDT 表中。

    这一章节就要详细描述第二步。

L21 内存分区与分页

  • 前面已经说过,将程序存储在内存中分为三步:

    1. 在编译阶段将一个完整的程序分为好几段;
    2. 在内存中找到空闲的空间来存储这些程序段
    3. 将每个程序段存储在找到的空闲内存中,并将基地址存储在进程控制块 PCB 的 LDT 表中。

    这一章节就要详细描述第二步。

内存分区

  • 内存分区可以固定也可以可变。

    • 对于固定分区,是在操作系统初始化时将内存等分为 K 个分区,然后将程序段存储在这些分区中。很显然固定分区存在不足,因为其分区大小固定不变,所以在存储程序段时不能随程序段大小的变化而变化,有可能存储不下大的程序段,也有可能因存储了小的程序段而浪费了内存。
    • 对于可变分区,是根据程序段的大小来改变分区的大小,所以操作系统采用的是可变内存分区。
★可变分区的管理
  • 如下图所示,可变分区有两张表:一张是已分配分区表,用于记录已经使用的内存空间;一张是空闲分区表,用于记录空闲的内存。

    • 当有一个程序段请求分配时,操作系统就会将请求程序段的大小和空闲内存的大小相比较,如果空闲内存可以存储下该程序段,就会根据空闲分区表选择一段空闲内存给该程序段,然后改变已分配分区表的内容和空闲分区表的内容。

    • 但内存中并不是一直往里存储程序段的,也有可能会释放内存,这时已分配分区表的内容和空闲分区表的内容就要进行改变。

    • 当有一个程序段再次申请空闲内存时,如果其有多个空闲内存块可以选择,就面临选择哪个空闲内存块的问题:

      • 如果选择首先适配,那就不需要将整个空闲分区表扫描一遍,执行速度快;
      • 如果选择最佳适配,那到最后就会产生一堆细小的空闲内存;
      • 如果选择最差适配,那到最后就会产生一堆均匀的空闲内存。

★★★内存分页

  • 内存分页的引入是为了解决内存分区导致的内存效率问题

    可变分区存在的问题:假设总空闲内存大小 >160K,但没有一个空闲内存分区大小 >160K,所以当有一个大小为 160K 的程序段想要存入内存中时,就存储不了,即使空闲内存大小是“足够”的,即存在内存碎片

    为了解决这个问题,就要将已经存储在内存中的程序段移动,使得最后空闲的内存能合并在一起,即内存紧缩。但内存紧缩一方面需要时间去移动程序段,另一方面,在程序段移动时,由于程序段在内存中的基址发生了变化,就会导致CPU无法正常工作,所以需要引入内存分页。

  • 内存分页就是针对每个程序段的内存请求,操作系统将空闲内存一页一页地分配给这个程序段

    所以现在的情况就是,★在操作系统初始化时,将内存划分成固定大小的一页一页(在 linux-0.11 中,一页大小为 4K ),在程序段需要分配内存时,操作系统就将空闲内存一页一页地分配给这个段,此时对于每个程序段,最大的内存浪费趋近于一页的大小

    内存分页有点类似于固定分区和可变分区的结合,内存被操作系统划分成固定大小的一页一页,但在将内存一页一页地分配给程序段时,最终的结果又是不同大小的程序段有着不同大小的内存,因为对不同大小的程序段,其内存页数不一定相等。

    ★★★注意:在前面用可变分区存储程序段时,虽然一整个程序被分成了多个程序段,但每个程序段存储到内存中时,是一整个存储的;但在使用了内存分页后,把程序段一页一页地存储在内存中时,即一个程序段又被划分成了多页离散地存储在内存中。

  • ★★★所以,为了找到正确的物理内存地址,就要引入页表,页表的使用和前面的段表类似。

    首先我们要知道某条具体的指令是在哪一页,以 mov[0x2240],%eax 为例,由于 linux-0.11 中每页大小为 4K,

    • 所以 0x2240 除以 4K,即 0x2240 右移 12 位(因为 4K = 2^12,所以除以 4K 相当于右移 12 位),得 2,所以是该程序段的第 2 页(页号为 2);
    • 然后 0x2240 对 4K 取余,得 0x240,所以是在该程序段第 2 页的基地址上偏移 0x240;
    • 又因为由页表可知,该程序段的第二页在内存中的页框 3 处(页框号为 3 ),所以在页框 3 的基址(3 乘以 4K,即 3 左移 12 位,得 0x3000)上偏移 0x240,最终得到逻辑地址 0x2240 的物理地址为 0x3240
  • 总结:一整个程序分为多个程序段(分段)-> 每个程序段分别存储在内存中(分区)-> 由于分区存在不足,所以将每个程序段又分为多页存储在内存中(分页)。

  • 注意:段表用于找到一个程序的每个程序段,而页表用于找到每个程序段的每个页。

---------------The End---------------