考研408《操作系统》复习笔记,第三章《3.2.2 内存分配:离散分配》

本来是打算把整个内存分配两大块:【连续分配】+【离散分配】一起写完笔记,但是发现【离散分配】复杂到离谱,只能分开写了,本章节是《连续分配》

一、离散分配(非连续分配)概念

1、为何要有离散分配?

说人话:因为【整块连续的进程】装入内存,肯定会有碎片。要利用这些碎片空间,那就把【进程拆分成不连续的小块】,离散分配内存

二、【分页】分配

1、分页的概念

  • 首先:进程全拆成以【块】为单位,内存也拆成以【块】为单位,它两的块大小固定且一样,有一一映射对应关系
  • 另外注意,关于这个【块】,进程和内存它两有专有名字的区分:
    • 【进程】的分配的单位叫【页】、【页面】、【Page】
      • 对应每个块的编号叫:【页号】、【页面号】
    • 【内存】的分配的单位叫【页框】、【页帧】、【物理页】、【物理块】、【内存块】、【PageFrame】
      • 对应每个块的编号叫:【页框号】、【页帧号】、【物理页号】
      • 【另外 注意地址单位】
        • 在这些分页分配的地址中,【页面(进程块)】、【页框(内存块)】的大小单位是按【字节B】为最小单位,注意区分【字节B ≠ 比特位b (bit)】
        • 然后【页】的大小是固定统一的,OS一旦确定后全部页面一样大小!!

2、映射关系数据结构【页表】

那么这两个块要怎么映射对应?

进程的【页面的逻辑地址】转化成内存里【页框的物理实际地址】的映射关系,被记录保存在【页表】这个数据结构里。而【页表】保存在【内核空间的PCB中】

这里我需要强调**【页】** 和**【页表里的页表项】**,不然后面会乱:

  • 【页表】一整块存放,他不会拆开!!!!
    • 所以【页表】里的【页表项】是连续存放的!!!
  • 【页】是把一个进程拆开的【各个块】
    • 所以【页】会拆散存放,不要搞混了!!!

【计算页表项大小】

一般题目就是给出【内存总容量:M】和【页 或 页框大小(两者相等):N】

  • 【页、页框要多少位地址】 :**【2^x = N】,则需要【x位】**二进制来表示N个地址
  • 【内存可以分配几个页框】1个页大小为N,则可分配【M / N】个页框
  • 那么**【块号需要多少位地址】** :页表要记录【M / N】个页框
    • 那么自然页面也得有【M / N】个页表项,也得有【M / N个块号】
    • 【2^k】=【M / N】,则需要【k位】 二进制来表示【M / N】个页框号
    • 然后由于【页号】是隐含的,就像数组【a9】,你无需存储这个9下标
      • 所以**【1个页表项】只用存储【块号】:假设1块是【kB】**
      • 整个页表若从【0~n】,则有【n+1】个页表项
        • 也就是**【k * (n+1)B】**

【快表(页表的副本)】

3、【逻辑地址结构】

1) 注意【页】和【页内地址】区别:

计算机组成原理《第三章(6)------Cache》学过:【块】和【块内地址】考研408《计算机组成原理》复习笔记,第三章(6)------Cache(超级重点!!!)_408cache-CSDN博客

  • 而其中的细节跟内存和进程的差不多
  • 一定要弄清楚**【1个进程的各个"整块页"可以离散存放】** ,但是**【"1个页"里面的内容是连续存放的】**
    • 注意:我引入《机组:cache和主存》地址转换是为了解释清楚这里【进程和主存】分区映射的特点,实际上【cache映射的 "块"】和【内存分配的 "页"】是完全不一样的
      • 前者为了存取数据快速而容量小,后者为了存够多进程数据而容量较大

2)【逻辑地址】的组成表达

这里提示,关于页号、页内偏移量的理解因人而异,我个人很讨厌用【页内偏移量】,所以我建议用【页内地址】、【页框内地址】来代替,会更方便理解。

就是:【页号】+【页内地址(页内偏移量)】

  • 【页号】代表【这个页是进程里第几块】
  • 【页内偏移量】代表【一个页内的具体一个位置】,【相对于这个页内的地址】 (比如下图蓝色的部分,也可以叫**【页内地址】、【页框内地址】**)

3)【逻辑地址的结构】和【页表项】关系

【逻辑地址】和前面刚学的【页表项】有什么关系?

  • 我们程序会给出一个一维【逻辑地址】,去到页表找到对应的【页表项】
  • 页表通过一定规则拆分逻辑地址,映射分析出对应的是第几个【页表项】

因此逻辑地址映射物理地址的第一步就是: "把**【逻辑地址】拆分出【页号】+【页内偏移量】**"

1、先按【二进制】视角分析
  • 若【一个页框大小M字节】,那么
    • 【1个页框需要k位地址】(2^k=M)
  • 若【一个内存大小L字节】,那么
    • 【内存共有y个地址】(2^y=L),然后
    • 【内存y个地址】可以分出【N个页框】(N = L / M = 2^y / 2^y)
    • 最终得出**【N个页框】需要【n位地址表示】(2^n=N)**
  • 然后可以发现:逻辑地址**【前n位:是页号】【后k位:是1个页地址长度】**
  • (一定要弄清楚这几个例子,很透彻)
2、再按【十进制】视角分析
  • 一般题目的地址给出的形式是【十进制】,则可以用十进制除法计算:
  • 至于为什么可以这样,你不用管,这是前人总结的规律

4、如何【逻辑】和【物理】映射

找到对应【找物理页框】

  • 现在已经根据【逻辑地址】找到了【页表项】,那就好办了
    • 【页表项】不就【页号】+**【块号】**吗
      • 现在找到了【页表项】,然后不就直接拿【块号】找【物理页号】就行了

找到【物理页框里具体地址映射】

  • 但是虽然你找到了内存对应的【物理页框】
  • 但是【页内地址】和【页框内地址】是对不上的啊!!
    • 那么只需要:【完整物理地址】=【物理页号】+【页内偏移量】
      • 因为**【物理页号】** 对应在**【内存哪个页框】**
      • 而**【页内偏移量】** 是**【相对一个页内的地址】** ,【页】和【页框】大小一样,相对位置当然不会变啊,直接用就行了

5、基本地址变换机构(硬件实现)

教材书完整表述:

教材图像:

分析每一句话:

  • 第一步:【逻辑地址】解析出【页号】+【页内偏移量】
    • 不多说,直接背这两公式行,一个得出**【页号】** 、一个得出**【页内偏移量】**
  • 第二步:【越界中断】,你这个逻辑地址的【页号】不能超过这个【页表的范围】啊
  • 第三步:找到【这个页号】在【页表对应的 页表项的位置】
  • 最后一步:利用【页表项的 "物理页号"】+【逻辑地址的 "页内偏移量"】得到完整【物理实际地址】

结合例题加深理解:

  • 例子1
  • 例子2
  • 例子3
  • 例子4

【其他博主的图,结合理解】

6、具有【快表】的【地址转换机构】

前面说过,页表的优化是【快表】,快表可以

  • 图像理解机构:
    • 其实就是多加了一个【快表TLB寄存器】
      • 初始情况,页表和快表都没有内容
      • 然后进程切换,PCB存入页表数据
      • 然后当第一次内存分配时,使用了页表,那么这个【页表项数据】同时就会被复制一份副本到【快表TLB】(以后每一次用到一次新的页表项都会同时备份到快表)
      • 那么当下次分析【逻辑地址】时,会先去缓存的【快表】查找,若找到了对应页号,就不用再去页表查找,直接去内存对应物理地址。没找到再去页表查找。
  • 而【具有快表的地址转换机构】也分两种
    • 一种是只支持【先查快表】、【快表未命中再去页表查】
    • 一种是支持【快表 和 页表 同时查询】
    • 要会根据两种系统,具体运行耗时是怎样计算的:
    • 【例子1】
    • 【例子2】

引入快表的明显的两个原理、原因:

  • 1、时间局部性:
    • 进程有的指令、数据很有可能会在【连续一段时刻】不断被访问
  • 2、空间局部性:
    • 进程有的指令、数据很有可能会在【连续的邻近空间】不断被访问

【对比两个硬件机构】

7、两级页表和多级页表

1)单一页表的问题

还没完!现在还有两个问题

  • 注意:【页表项】有自己的大小,跟【页的大小】没有关系,只有跟【页的数量】有关系,
    • 比如上面例子定死了一个【页表项4B】,【页多大】都不影响一个【页表项大小】
    • 但是,【页的数量】多则【页表项数量】也多,【页表总体积】就大

2)解决思路:分级页表

  • 那么【进程】太大了,我们可以拆分成多个【页】离散存放
  • 【页表】太大了 ,我们一样可以拆分成多个【页】 离散存放
    • 【页表】统一记录了【进程各个页】的地址映射关系
    • 那只需要再额外加一个【索引表】,就可以让**【索引表】统一记录了【页表各个页】的地址映射关系**
      • 为了区分【页表的页】和【进程的页】,后续我尽量用**【页表块】这个说法代表【页表的页】**
      • ​​​​​​​还有2个误区!!!
        • 1、【页表的页(页表块)】=【连续几个页表项】≠【1个页表项】
        • 2、但是不管你页表、进程、内存页框,反正你能【分页】,就必须按统一的大小分!!!分页的规则就是拆分的所有【页(块)】都要一样大
          • 所以**【页表块(页表页)】=【进程页】=【内存页框】**

3)两级页表的【逻辑地址结构】

  • 两级页表的【逻辑地址】就是把【页号】又细分成【页目录号】+【页号】
    • 其中【页目录号】指的是【索引表的各个表项】
      • 【索引表的表项】记录的是每一个【页表块(页表的页)】在哪
    • 【页号】指的是1个页表块里的【页表项】
      • 【页表项】记录的是每一个【逻辑页 和 物理页框号 的映射关系】
    • 【页内地址】指的是具体在【页 和 页框内的相对位置】

如何计算【逻辑地址】的【页目录号】、【页号】、【页内地址】位数

4)多级页表(按二级页表思路类推)

【例题】

【例题】

三、【分段】分配

1、为什么要分成【段】?分块不就够了?

人话:

  • 【分页】每个【页】都是【固定一样大小】!!!
    • 而且每个【页】里包含了各种【可共享、不可共享】的不同权限的内容,不够灵活
  • 【分段】每个【段】都【不一样大】!!!
    • 各个【段】有自己内容类型所带的特殊权限、不同功能,够灵活
      • 尤其是第二章、第三章我们都学过,【进程映像】里包含了各个【段】

【具体4大优势】

  • 1、便于分段设置【权限管理、保护】
  • 2、便于【存储共享】
  • 3、【段名】便于程序员编程时区分、管理各个段
    • 在程序员视角可以用【段名】区分、指明具体段
    • 操作系统实际运行时仍用【段号】区分、管理
  • 4、便于【动态链接和增长】
    • 前面动态链接学过,暂时不需要的模块可以先不链接到内存
    • 那么【分段】正是按逻辑分各个代码段、数据段,有的可能暂时不用链接到内存

2、【段】的【逻辑地址结构】

注意重点:

  • 因为【段】不是一样大小的,所以段的【逻辑地址】必须是二维的!!
    • 结构上其实跟【分页】一样,也是:【段号】+【段内偏移量】
    • 区别在于【内在含义】:
      • 【分页】:可以直接用【完整的一维逻辑地址】,固定【前n位:页号】、【后k位页内偏移量】
      • 【分段】:不可以用【二维逻辑地址】固定分析出【段号】、【段内偏移量】,因为段大小不一样,地址位数自然也不一样

3、映射关系的数据结构【段表】

简单说就是【页表项】=【段号】+【段在内存起始地址】+【段长】(实际段号不存储)

【硬件】

【对比分段、分页】

【逻辑结构】一个一维、一个二维

【例题】

三、【段页式】分配

就是把【分段】和【分页】结合起来而已

细节如下图,我写笔记累了,不想打字了

【例题】


最难的一题:

  • 第一大难点:【多级页表】里【页表页大小相同】,一级页表、二级页表、进程页...都是一样大,页表项也一样多;然而你可以选择最少使用前几块页表项,比如下图一级页表只选择了前128个页表项记录,二级页表则用满了整个页表记录
  • 第二大难点:【一级页表的1个页表项】对应【二级页表的1个页】!!!【二级页表的1个页表项】对应【进程的1个页】!!!!