文章目录
非连续分配管理
- 支持多道程序的两种连续分配方式的缺点
- 固定分区分配 : 缺乏灵活性, 产生了许多无法利用的内部碎片
- 动态分区分配 : 当产生了许多外部碎片时, 需要采用"紧凑"技术, 时间代价高
如果允许将一个进程分散地装入到许多不相邻的分区中, 便可充分利用内存, 而无需再进行“紧凑” ---- 分连续分配方式
基本分页存储管理
- 思想 : 把内存划分为一个个
相等的小分区, 然后按照分区大小将进程拆分成一个个小部分. - 页框 : 内存空间划分的一个个大小相等的分区
- 页框号 : 每个页框的编号, 从0开始 (低地址开始)
- 页面 : 将用户进程的地址空间划分为和页框大小相等的一个个区域
- 页号 : 页面的编号, 从0开始
- 进程的最后一个页面可能没有页框大, 因此
页框不能太大, 否则可能产生过大的内部碎片 -
进程的页面, 和内存的页框一一对应, 页面不必连续存放, 也不必按先后顺序.
如何实现地址转换
-
采用分页技术后, 进程的地址空间会被分为一个个页面, 这些页面离散的, 不按顺序地放入内存的各个页框当中. 因此逻辑地址->物理地址的转换, 成了需要解决的第一个问题
-
连续分配中采用动态重定位进行地址转换
- 运行时进行地址转换, 一个重定位寄存器记录进程的物理首地址. 实际地址 = 逻辑地址 + 重定位寄存器中的物理首地址
- 模块在内存中的
起始地址+ 目标内存单元相对于起始位置的偏移量
-
前20位对应页号, 后12位对应页内偏移量 -
如果每个页面大小为2^kB, 用二进制表示逻辑地址时, 末尾k位是 页内偏移量 , 其余部分就是页号
页表
- 为了能知道进程的每个页面在内存中的存放位置, 操作系统要
为每个进程建立一张页表- 一个进程对应一张页表
- 一个页面对应页表中的一个页表项
- 每个
页表项由“页号” 和 “块号” 组成 - 页表记录
进程页面和实际存放的内存块之间的关系 每个页表项的长度是相同的, 页号是"隐含"的
为什么每个页表项的长度相同, 页号是隐含的
- “隐含” 是指不需要显示存储页号, 第几个页块也就对应了第几个页号, 所以内存页表中只存储了块号地址. 想要直到M号页面对应的物理起始地址, 直接用M*3+页表起始地址, 找到M号页面所属的页表项的位置, 然后读取其中的块号, 然后用 块号 * 页面(页框)大小, 即可知道该页面实际存放的物理地址.
总结
基本地址变换机构(页表寄存器)
- 分页存储管理 : 将内存划分为一个个大小相同的分区, 称为“页框”, 将进程的地址空间划分为相同大小的一个个模块, 称为“页面”, 页面和页框一一对应, 这些页面离散的, 不按顺序地存储在也页框中. 寻址的时候, 只需要根据逻辑地址, 计算出所在的页号, 然后查找页表取出该页号的页面在内存中存放的物理首地址. 然后求出逻辑地址在该页面的偏移量. 用首地址+偏移量. 就是该逻辑地址的物理地址. 一个进程对应一个页表, 一个页面对应一个页表项. 页表项由页号和块号组成. 页号是隐含的. 逻辑地址对应的页号 由 逻辑地址 / 页面大小. 偏移量 逻辑地址 % 页面大小.
- 系统中会设置一个
页表寄存器(PTR), 存放页表在内存中的起始地址F 和 页表长度M,- 进程未执行时, 页表的起始地址 和 页表的长度(
多少个页面) 放在进程控制块(PCB)中, 当进程被调度时, 操作系统内核会把他们放到页表寄存器中- (因为是
动态重定位的方式进行地址转换)- (所以说页表寄存器也只有一个, 所有继承共享, 运行的进程才能占用)
- (因为是
- 进程未执行时, 页表的起始地址 和 页表的长度(
- 因为页内偏移量是末尾多少位, 块号是前面多少位, 两个可以直接拼接在一起.
- 1023 是 011 11111111; 1024 是 100 00000000
-
页内偏移量占10位, 说明最大是 11 11111111个存储单元, 所以一个页面大小100 00000000存储单元. 因为按字节寻址, 所以一个存储单元大小是一个字节. 所以一个页面大小1kB. 如果是按字存储, 那么一个页面大小就是2kB.
-
分页存储管理, 只需要知道
页面大小, 就可以知道逻辑地址对应的物理地址. 所以说地址是一维的.- 页号 : 逻辑地址 / 页面大小
- 偏移量 : 逻辑地址 % 页面大小
- 块大小 : 页面大小
- 页表起始地址系统分配,
- 块号 : 页表起始地址 + 块大小 + 页号
- 物理地址 : 块号 * 块大小(页面大小)
-
需要注意对页号的越界检查 (页号和页表长度进行比较, 只有小于 才合法)
-
页表也是存储在内存中的, 也需要考虑碎片问题
- 如果一个页表项长度3B, 而页面大小4kB, 会有1B的内部碎片. 同时对页表项存放的物理地址的寻址也会进行特殊处理
- 所以为了寻址方便, 通常会让一个页表项占更多的字节.
总结
- 整个寻址过程中, 两次访问内存
- 第一次 : 查询页表
- 第二次 : 访问目标存储单元
具有快表的地址变换机构
局部性原理
-
时间局部性 : 如果执行了程序的某条指令, 不久将来, 这条指令很可能再次被执行. 如果访问了某个数据, 不久将来, 这个数据会再次被访问. ( 因为程序中存在大量循环 )
-
空间局部性 : 一旦程序访问了某个存储单元, 不久之后, 其附近的存储单元也很有可能被访问 (因为很多数据在内存中是连续存放的)
-
在基本地址变换机构中, 每次访问一个逻辑地址, 都需要
查询内存中的页表, 由于局部性原理,可能连续多次查到的都是同一个页表项. 既然如此, 能否利用这个特性减少访问页表的次数?
快表
- 快表:
联想寄存器(TLB), 是一种访问速度比内存快很多的高速缓冲存储器. 用来存放当前访问的若干页表项, 以加速地址变换的过程. 与此对应, 内存中的页表常称为慢表
引入快表后, 地址变换过程
- 默认先查询快表, 失败后在查询慢表
总结
- 如果快表查询没有命中, 需要查询慢表, 同时把对应的页表项
复制一份到快表中 - 访问内存速度是比访问快表慢的
两级页表
单级页表存在的问题
-
页表必须连续存放(方便根据页号查询), 因此当页表很大时, 需要占用很多个连续的页框 -
没有必要让整个页表常驻内存, 因为进程在一段时间内可能只需要访问某几个特定页面(局部性原理)
-
我们是如何解决进程在内存中必须连续存放的问题的
- 将进程地址空间分页, 用一个页表记录个页面的位置
-
同样思路, 用于解决“页表必须在内存中连续存放的问题”, 把连续存放的页表再也页. 然后用一个
数据结构, 记录页表在内存中的位置- 将长长的页表进行分组, 使每个内存块刚好放入一个分组 (上个例子中, 每个页面4kB, 每个页表想4B, 每个页框可存放1k个页表项, 因此每1k个连续的页表项为一组, 每组刚好占用一个内存块, 再将各组离散地放到不同内存块)
- 要为离散存储的页表再建立一张页表, 称为
页目录表,外层页表,顶层页表
两级页表 (解决连续占用内存问题)
- 一级页表内存块号 指的是 对应的二级页表在内存中存放的位置. 取出来后, 继续在这个二级页表中查询指令存放的块号.
虚拟存储技术 (解决整个表常驻内存的问题)
需要注意的细节
- 若采用多级页表机制,
各级页表的大小不能超过一个页面 - 单级页表只需要进行两次内存访问
- 两级页表需要进行三次, 虽然解决了单级页表的存在的两个问题, 使得内存利用率提升, 相对应付出的代价是, 多一次访问内存, 花费更长的时间
- 没有快表, n级页表, 需要访问n+1次内存
- 没有快表, n级页表, 需要访问n+1次内存
总结
基本分段存储管理
分段
- 进程的地址空间, 按照程序
自身的逻辑关系划分为若干个段, 每个段都有一个段名(低级语言中, 程序员使用段名来编程), 每段从0开始编址 - 内存分配规则 : 以段为单位进行内存分配,
每个段在内存中占据连续存储空间, 但各段之间可以不相邻
- 段号的位数 决定了 每个进程最多可几个段
- 段内地址的位数 决定了 每个段的最大长度是多少
段表
- 程序分为多个段, 各段离散存储在内存中, 为了保证程序的正常运行, 就必须能从内存中找到各逻辑段存放的位置, 为此需要一张段映射表, 简称“段表”
- 每个段对应一个段表项, 其中记录了该段在内存中的起始位置(基址) 和 段的长度
- 各个段表项的长度是相同的, 因此段号可以隐含, 不占存储空间.
地址变换
- 进程未上处理机运行时, 段表在内存的起始地址 和 段表长度 存放在PCB中. 进程上处理机运行后, 这些信息会转移到
段表寄存器中(一个物理硬件) - 分页存储中, 各页大小一样, 不需要对页内偏移量是否越界进行检查; 而分段存储中, 各段大小不同, 需要对段内偏移量是否越界进行检查 (和段长比较)
分段, 分页管理的对比
- 分页地址转换是一维的 : 只需要一个页面大小
- 分段地址转换是二维的 : 需要段号, 和段内偏移量 (段名 和 助记符)
总结
段页式管理
分页, 分段的优缺点分析
分段 + 分页 = 段页式
- 地址结构也是二维的 : 用户只需要提供段号 和 段内地址 (系统自动将段内地址 拆分成 页号, 页内偏移量)
段表, 页表
- 一个进程对应一个段表, 多个页表
地址转换过程
- 需要检查页号是否越界, 因为各段长度不等, 页表程度也就不同.