本来是打算把整个内存分配两大块:【连续分配】+【离散分配】一起写完笔记,但是发现【离散分配】复杂到离谱,只能分开写了,本章节是《连续分配》
一、离散分配(非连续分配)概念
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】**
;
;
【快表(页表的副本)】
- 【页表(慢表)】存储在【内存】,访问内存虽然比访问外存快,但是也相对较慢
- 【快表】存储在【高速缓存】,它可以复制页表的内容,代替页表加快地址查询速度
- 【页表】是 "页表的优化"、"页表的副本"
- 更多【快表】具体内容在《计组》笔记:考研408《计算机组成原理》复习笔记,第三章(7)------虚拟存储器_tlb表项408-CSDN博客
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个页】!!!!





















































































































