MOS_Chapter3_MemoryManagement
3.1无存储器抽象
BIOS Basic Input Output system 存在于ROM中
静态重定位: 当一个程序被装载时, 该地址为程序的初始地址, 所以程序地址都会加上这个首地址当作使用时的地址
3.2存储器的抽象:地址空间
解决以下问题
- 物理地址直接暴露会使用户对内存可寻址,容易故意破坏操作系统
- 使用无存储器抽象同时运行多个程序是很困难的
3.2.1 地址空间
地址空间解决的问题: 多个应用程序同时存在于内存中容易造成地址混乱/互相读写等问题,核心问题保护和重定位
什么是地址空间?
一种存储器的抽象,类似进程与CPU的关系
是一套地址集合, 进程可以用它来寻址内存
如何让每个程序都有自己的一套地址空间?
动态重定位
什么是动态重定位?
简单地把每个进程的地址空间映射到物理内存不同部分, 造成每个人都有自己的一套地址空间的假象
动态重定位实现的硬件条件是什么?
基址寄存器base register 和 界限寄存器 limit register
基址寄存器: 装载进程程序的起始物理地址
界限寄存器: 装载程序的长度
动态重定位实现的过程?
- 一个进程访问内存, 取指令或者读取数据, CPU把地址发给内存总线
- 自动把基址寄存器内的值加到进程发出的地址上
- 检查地址是否大于界限寄存器的值
3.2.2 交换技术
因为将多个程序同时载入内存, 但实际上内存的总容量是有限的, 有两种方式解决内存超载: 交换swapping 和 虚拟内存 virtual memory
什么是交换技术?
把一个进程完整的调入内存, 运行一段时间后又把它调回磁盘
什么是虚拟内存?
使程序在只有一部分被调入内存的情况下运行
什么是内存紧缩memory compaction?
交换会在内存产生多个空闲区hole, 将这些小的内存空间/碎片合并, 但通常因为花费时间长一般不使用这个技术
当进程被创建或换人时应该为它分配多大的内存?
固定大小的内存不需要多考虑
一般使用动态内存技术灵活分配内存
动态分配内存遇到的问题:
-
进程与空闲区相邻, 可以直接把空闲区分配给它
-
进程与进程相邻, 则需要把该进程复制到更大的一块内存中去.
为了减少进程交换与移动的开销使用冗余方法, 额外非陪一下内存
3.2.3 空闲内存管理
对于空闲的内存我们关注几个问题
-
用什么存储
-
怎么分配大小
位图存储管理: 内存单元映射到位图中, 0表示空闲, 1表示占用. 分配的单元越小, 单元数量就越多, 因此位图就越大, 容易造成浪费
链表存储管理: 维护一个已分配内存段和空闲内存段的链表. 一个结点表示一个进程/两个进程间的空闲区域
当按照地址顺序在链表中存放进程与空闲区时, 为进程分配内存的几种算法:
- 首次适配first fit: 沿链表搜索, 第一次遇到足够大的空间, 切割一样大小的内存给进程, 剩余空间作为一个空闲块
- 下次适配 next fit: 每次找到能容纳进程的空闲区,记录下来位置, 下次寻找空闲区时从该位置开始寻找
- 最佳适配best fit: 遍历整个链表, 找到最小的
- 最差适配worst fit: 遍历整个链表, 找到最大的
- 快速速配quick fit: 维护固定大小的空闲区, 拿来就用
3.3 虚拟内存
当软件不断使用,管理的数据就非常大, 即使基址和界限寄存器能解决抽象问题, 但无法解决一次性读入整个进程的过多数据
上世纪使用的方法覆盖overlay: 把程序分割成许多片段, 覆盖管理模块载入内存后不断将覆盖之前运行的程序片段, 换入换出非常耗时
虚拟内存virtual memory: 每个程序拥有自己地址空间, 这个空间被分割成多个块, 叫做页面page. 每一个页面有连续的地址范围并被映射的物理内存.
不是所有页面被装入时才能运行因为当程序应用到映射于物理内存的地址空间时, 硬件执行此映射. 如果该地址不在物理内存中, 操作系统则会把页面调入物理内存, 使程序继续执行.
3.3.1 分页paging
先了解几个概念
虚拟地址virtual address: 由每个程序自己产生的地址
虚拟地址空间virtual address space: 由这些地址构成的地址空间
内存管理单元MMU: 虚拟地址没被直接送到内存总线上, 而是通过内存管理单元映射为物理内存地址后再传给内存总线
页框page frame: 虚拟地址空间按照固定大小进行划分成页面page, 物理内存中对应的物理单元叫做页框 两者大小通常一样.
如果通过页面映射到了页框, 皆大欢喜直接读取并执行
如果没有映射到, CPU陷入Trap操作系统称为缺页中断/缺页错误page fault, 把一个少用/空闲的页框写入磁盘, 把页面写到页框中, 修改映射关系.
MMU一般以页表page table作为索引获得虚拟页面对应的页框的号
3.3.2 页表
一直说说说, 话好像有点多,千言不如一图
下面是个页表项
虚拟地址到物理地址的映射:
虚拟地址被分成虚拟页号和偏移量, 指定页面和页面中的具体位置
虚拟页号可以用作页表的索引来找到页表项, 由页表项找到页框号, 把页框号拼接到偏移量之前 代替虚拟页号
3.3.3 加速分页过程
运用分页技术需要考虑的两个问题.
速度和大小
- 虚拟地址到物理地址的映射必须非常快
- 虚拟地址空间很大, 页表也会很大(类似一个虚拟空间对应一个程序的页表)
转换检测缓冲区
为了实现加速分页机制, 我们使用了一种技术转换检测缓冲区Translation Lookaside Buffer TLB/相联存储器/快表, 它利用了一个小型的硬件设备将虚拟地址直接映射到物理地址
TLB的工作机制
来个流程图帮助解释
如果还要加一层判断,那就是关于缺页中断, 如果页表中也没有,则需要到磁盘中读取进来
对于各个变量之间的映射
3.3.4针对大内存的
- 多级页表
其实就是套娃页表
顶级页表的表项含有二级页表的地址/页框号
- 倒排页表inverted page table
本质上通过物理地址作为索引寻找虚拟地址
优点:节省空间,物理页框的数目比较固定
缺点: 正常的映射- 虚拟地址到物理地址的转换
解释一下: 因为多个虚拟地址可能重复写一个物理地址, 因此把物理地址拿出来, 映射到相同物理地址的虚拟地址就排成一条链
3.4 页面置换算法
3.4.1最优页面置换算法
实现: 在缺页中断发生时, 计算每个页面下次发生的时间, 以它给页面进行标记 把最晚的那页淘汰掉
缺点:现实中无法实现, 因为人为因素, 无法预知某个页面可能突然被使用
优点:当然是最优的
3.4.2最近未使用页面置换算法NRU
状态位
系统为每个页面设置两个状态位
- R位 被读/写
- M位被修改
根据这两个值分成4类状态
- 第0类 R=0,M=0
- 第1类R=0,M=1
- 第2类R=1,M=0
- 第3类R=1,M=1
算法实现:随机从编号最小的非空类挑一个页面淘汰.当一个进程启动时,索引页面R和M清0 R位每次时钟中断(定期)清0
优点:易于理解,容易实现,性能足够
3.4.3先进先出置换算法
FIFO算法
实现: 由操作系统维护一个所有当前在内存中页面的链表, 最新进入的页面放入表尾部. 缺页中断时,淘汰表头.
3.4.4 第二次机会second chance
对FIFO修改, 给页面第二次机会.
检查老页面的R位
如果是0, 又老又没被使用, 立即淘汰
如果是1 将R位清0, 将其放到表尾 直到它在表头被淘汰
3.4.5时钟页面置换算法
与second chance相比, 第二次机会经常移动页面降低了效率, 一个好方法是不移动页面, 只移动指针.
实现: 当发生缺页中断时, 检查当前指向的页面
R=0, 淘汰
R=1, 清除R并指向next
3.4.6 最近最少使用页面置换算法LRU
实现: 在缺页中断发生时, 置换未使用时间最长的页面, 因为该页面未来一段时间可能仍然不会被使用
一般需要一个特殊的硬件帮助实现
3.4.8工作集页面置换算法
请求调页demand paging: 开始内存里是没有页面, 因此请求一次,中断一次.因为时调入
工作集work set: 一个进程当前使用页面的集合, 表现为局部性访问**
颠簸Dennning: 每执行几条指令就缺页中断
预先调页prepaging: 进程运行前预先装入其工作集页面到内存
工作及算法实现: 找出一个不在工作集的页面, 并淘汰.
过程:
硬件设置R,M位, 每个时钟滴答都会清除R位, 缺页中断时淘汰一个页面
R=1, 标识并不删除
R=0, 当前时钟滴答中没访问过,可以替换. 再考虑生存时间, 大于 淘汰, 小于 留下
3.4.9 工作集时钟页面替换算法
类似上面的时钟算法和工作集算法
循环一周后说明要么至少一次, 要么一次调度都没有
以页框为元素的单位, 分别是表项上次使用的时间, R位, M位
3.5 分页系统的设计
上面考虑的是分页系统的实现与工作, 下面讨论分页系统设计时的一些问题与技术
3.5.1 局部分配与全局分配策略
核心问题: 怎么在相互竞争的可运行进程之间分配内存
局部: 指分配给该进程的内存页面中, 有效为每个进程分配固定的内存片段
- 缺点: 有大量空闲页仍然会颠簸. 工作集缩小又会导致浪费内存
全局: 所有内存页面中, 在进程之间动态分配页框
- 系统必须不断确定给每个进程分配多少页框来实现动态分配
- 方法1: 监测工作集大小 不能方式颠簸因为大小变换可能非常快, 而用aging位检测的时钟滴答要慢得多
- 方法2:使用页框分配算法, 定期确定进程的页框数, 再分配页
缺页中断率算法PFF Page Fault Frequency: 指出何时增加/减少一个进程的页面, 仅控制大小, 不决定替换页面的对象
3.5.2 负载控制
减少竞争内存的进程数来满足部分进程内存需求, 将一部分进程交换到磁盘释放其内存页面, 让它的内存可以被颠簸状态的进程分享
3.5.3 页面大小
内碎片internal fragmentation:页面内被浪费的空间
外碎片 external fragmentation:内存块/段之间的空闲区, 可通过内存紧凑解决
小页面策略优点
- 内部碎片浪费减少
- 运行时占用的内存少
- 充分利用TLB空间
- 从磁盘获取的时间与大页面基本相同, 大部分时间花在寻道和旋转延迟上
3.5.4 指令和数据空间分离
分别设置指令地址空间和数据地址空间, 两者都可以进行分页并相互独立
现在的地址空间经常被划分到一级缓存里,而不再分给常规的地址空间? 不太懂
3.5.5 共享页面
避免几个不同用户同时使用一个程序形成的页面副本
写时复制:有点类似fork的情况, 只要不写就用同一份
3.5.6 共享库
划重点, 共享库DLL动态连接库
当一个共享库被装载和使用时,整个库并不是被一次性地读人内存。而是根据需要,以页面为单位装载的,因此没有被调用到的函数是不会披装载到内存中的.
位置无关代码position-independent code: 只使用相对偏移量(总是向前/后跳转n个字节, 不是直接给出跳转地址)的代码避免使用绝对地址(两者的基址在两者的地址空间中不一致).
解决被不同进程定位到不同地址的重定位问题
3.5.7 内存映射文件memory-mapped file
进程通过系统调用将一个文件映射到其虚拟地址空间的一部分而不实际读入页面的内容, 只有当访问时才会读入
两个以上进程映射到同一文件时可以通过共享内存来通信
3.6 虚拟内存的实际问题
3.6.1 分页工作
分页工作的处理时机
- 进程创建时
- 进程执行时
- 缺页中断
- 进程终止时
3.6.2 缺页中断的处理
缺页中断发生时
- 硬件陷入内核, 在堆栈中保存程序计数器
- 启动汇编代码例程保存通用寄存器等信息
- 发生中断, 确定目标虚拟页面
- 发现发生中断的虚拟地址, 并检查保护位等, 再检查是否有空闲页如果没有使用页面替换算法
- 如果选择的页框"脏", 就写回磁盘, 进行上下文切换, 挂起缺页中断的进程, 让其他进程运行至磁盘传输结束再来运行该进程
- 一旦页框"干净",通过磁盘操作装入页面, 挂起进程让其他进程运行
- 磁盘中断发生时表明页面已被装入, 更新页表和页框
- 恢复缺页中断指令以前的状态, 程序计数器指向指令
- 调度缺页中断的进程和汇编语言例程
- 例程恢复寄存器和其他状态信息并返回到用户空间
3.6.6策略和机制分离
存储器页面管理的策略与机制分离
底层MMU处理程序处理映射
内核中缺页中断处理程序处理虚拟页面并告诉外部页面调度程序
用户态外部页面调度程序从磁盘中请求页面
3.7 内存分段
什么是分段segment?
每个段由从0到最大的线性地址序列构成, 可以有不同长度的段. 每个段可以构成独立的地址空间
3.7.1 纯分段
将完全划分为一段一段的, 容易出现外碎片
3.7.2 分段和分页结合
MULTICS
因此虚拟地址被分为三部分
段号+页号+页内偏移量
Exercise
2.在图3-3中基址寄存器和限界寄存器含有相同的值16384,这是巧合还是它们总是相等?如果这只是巧合,为什么在这个例子里它们是相等的?
巧合
基址寄存器是载入程序到内存后的程序的起始地址
界限寄存器是该段内存的最大值
总的来说就是 起始地址是16384 程序的长度也是16384
4.在一个交换系统中,按内存地址排列的空闲区大小是10MB、4MB、20MB、18MB、7MB、
9MB、12MB和15MB。对于连续的段请求:
(a) 12MB
(b) IOMB
(c)9MB
使用首次适配算法,将找出哪个空闲区?使用最佳适配、最差适配、下次适配算法呢?
首次适配 | 最佳适配 | 最差适配 | 下次适配 | |
---|---|---|---|---|
12 10 9 | 20 10 18 | 12 10 9 | 20 18 15 | 20 12 9 |
5.物理地址和虚拟地址有什么区别?
形式上: 都是页号+业内偏移 没啥区别
地址范围上: 物理地址 映射到不同且固定的内存中 虚拟地址可能映射到相同,不固定的内存上
数量上: 物理地址有范围,范围大小与内存大小相关. 虚拟地址可以无限大, 但映射到的物理内存是有限的,因此与物理内存无关
真实内存使用物理地址。 这些是记忆中的数字芯片在总线上做出反应。
虚拟地址是引用的逻辑地址到进程的地址空间。 因此具有 32 位字的机器可以生成虚拟地址高达 4 GB,无论机器是否有更多或小于 4 GB 的内存
6.对下面的每个十进制虚拟地址,分别使用4KB页面和8KB页面计算虚拟页号和偏移量:20000,32768, 60000。
4KB: 因为考虑到计算机地址的特性 从0开始 因此都减1
20000/(4*1024) = 4+3616 = 3_3615
32768/(4*1024) = 8+0 = 6 + 4095
60000= 14+2656 = 13 2655
7.使用图3-9的页表,给出下面每个虚拟地址对应的物理地址:
(a)20
(b)4100
(c)8300
0+20 -> 8K+20 = 8*1024+20= 8212 = 8212
0+4100 ->8192+4100= 12292
8300->1*4K+4204-1->4k+4204 =8300
9.为了让分页虚拟内存工作,需要怎样的硬件支持?
MMU 内存映射单元 完成虚拟地址到物理地址的映射
trap机制保证完成不了映射时从磁盘中读页
10.写时复制是使用在服务器系统上的好方法,它能否在手机上起作用?
写时复制: 不写入时共享同一块内存, 写时再复制一块内存
可以起作用. 手机支持多道程序设计, 一个进程创建子进程时 会共享内存
15.假设一个机器有48位的虚拟地址和32位的物理地址。
(a)假设页面大小是4KB,如果只有一级页表那么在页表里有多少页表项?请解释。
4K= 2^12 页内偏移 = 12 48-12 = 36 2^36
(b)假设同一系统有TLB,该TLB有32个表项。并且假设一个程序的指令正好能放入一个页,其功能是顺序地从数组中读取长整型元素,该数组存在上千个不同的页中。在这种情况下TLB的效果如何?
指令地址命中率100% 在TLB中: 每次读数组都要用到他 因此100%
数据页会100%命中 直到程序读入下一个数据页: 从1024个数据中找 直到访问下一页 同时从外部读取
4KB包含1024个长整型 因此每1024个数据引用 总有一次miss 和额外内存访问
16.给定一个虚拟内存系统的如下数据:
(a)TLB有1024项,可以在1个时钟周期(1ns)
内访问。
(b)页表项可以在100时钟周期(100ns)内访问。
(c)平均页面替换时间是6ms
如果TLB处理的页面访问占99%,并且0.01%的页面访问会发生缺页中断,那么有效地址转换时间是多少?
0.991ns+ 0.0099 * (1+100) +0.0001 * (6 * 10001000+101) = 602.00000000001
17.假设一个机器有38位的虚拟地址和32位的物理地址。
(a)与一级页表比较,多级页表的主要优点是什么?
可以容纳更多的页表项, 地址空间更大,
减少了内存需要物理页的页数, 因为分层结构 映射到相同物理页的更多
事实上 多级页表没有那么多分级:通常一个指令页 一个数据页 一个顶级页表即可
(b)若采用二级页表,页面大小为16KB,每个页表项为4字节,应该对第一级页表域分配多少位?对第二级页表域分配多少位?请解释原因。4字节 14-2 =12
38 - (4+10) = 24 用于二级页表 24-(12) = 14 一级:12 二级 102
19.一个32位地址的计算机使用两级页表。虚拟地址被分成9位的第一级页表域、11位的二级页表域和一个偏移量,页面大小是多少?在地址空间中一共有多少个页面?
32-9-11 = 12 4KB大小 2^20个
23.TLB需要的相联内存设备如何用硬件实现?这种设计对扩展性意味着什么?
相联映射设备事实上比较与多个寄存器一个键, 每个寄存器都有一组比较器来比较寄存器的每一位与键的差别.
需要晶体管的数量是一个寄存器线性的函数 . 所以扩展非常的昂贵
24.一台机器有48位虚拟地址和32位物理地址,页面大小是8KB,如果采用一级线性页表,页表中需要多少个表项?
48 - 3-10 = 35
25.一个计算机的页面大小为8KB,主存大小为256KB,64GB虚拟地址空间使用倒排页表实现虚拟内存。为了保证平均散列链的长度小于1,散列表应该多大?假设散列表的大小为2的幂。
物理页数 256KB/8KB =32 页 32 的散列表链长=1 32*2 = 64 散列表大小
如果物理主存是256GB 则答案
256*1024 KB/8KB =32768pages 32768 * 2 因为是2的幂 保证链长=0.5
28.如果将FIFO页面置换算法用到4个贡框和8个页面上,若初始时页框为空,访问序列串为0172327103,请问会发生多少次缺页中断?如果使用LRU算法呢?
IFIO 6次
0 | 1 | 2 | 3 | sum |
---|---|---|---|---|
0 | 1 | |||
1 | 0 | 2 | ||
7 | 1 | 0 | 3 | |
2 | 7 | 1 | 0 | 4 |
3 | 2 | 7 | 1 | 5 |
3 | 2 | 7 | 1 | 5 |
3 | 2 | 7 | 1 | 5 |
3 | 2 | 7 | 1 | 5 |
0 | 3 | 2 | 7 | 6 |
0 | 3 | 2 | 7 | 6 |
LRU=7 同理 不过会将hit的重新读到第一个寄存器中 0172327103
x | 0 | 1 | 7 | 2 | 3 | 2 | 7 | 1 | 0 | 3 |
---|---|---|---|---|---|---|---|---|---|---|
x | x | 0 | 1 | 7 | 2 | 3 | 2 | 7 | 1 | 0 |
x | x | x | 0 | 1 | 7 | 7 | 3 | 2 | 7 | 1 |
x | x | x | x | 0 | 1 | 1 | 1 | 3 | 2 | 7 |
30.一台小计算机有4个页框。在第一个时钟周期时R位是0111(页面0是0,其他页面是1),在随后的时钟周期中这个值是1011、1010、1101. 0010、1010、1100、0001.如果使用带有8位计数器的老化算法,给出最后一个时钟周期后4个计数器的值。
页框 | ||||||||
---|---|---|---|---|---|---|---|---|
0 | 0 | 1 | 1 | 0 | 1 | 1 | 1 | 0 |
1 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 1 |
2 | 0 | 0 | 1 | 1 | 0 | 1 | 1 | 1 |
3 | 1 | 0 | 0 | 0 | 1 | 0 | 1 | 1 |
35.从平均寻道时间10ms、旋转延迟时间10ms 每磁道32KB的磁盘上载入一个64KB的程序,对于下列页面大小分别需要多少时间?
(a)页面大小为2KB。
(10+10 * (32/2)) * 2 = 340ms
正确答案:所有2KB页 0.009766 ms +旋转延迟 64/2 *10 + 10 寻道时间 = 320.21
(b)页面大小为4KB。
假设页面随机地分布在磁盘上,柱面的数目常大,以致于两个页面在同一个柱面的概率可以忽略不计
4KB页 0.01953 +10+64/16*10 = 160.3125
总之这个所有页的传输时间也太抽象了
36.一个计算机有4个页框,载入时间、最近一次访问时间和每个页的R位和M位如下所示(时间以一个时钟周期为单位):
页面 | 载入时间 | 最近一次访问时间 | R | M |
---|---|---|---|---|
0 | 126 | 280 | 1 | 0 |
1 | 230 | 265 | 0 | 1 |
2 | 140 | 270 | 0 | 0 |
3 | 110 | 285 | 1 | 1 |
(a)NRU算法将置换哪个页面?
2 R=0 未使用 M=1 修改过 看R M
(b)FIFO算法将置换哪个页面?
3 看载入时间
(c)LRU算法将置换哪个页面?
1 看R=0 和访问时间 尽管修改过 但看的是最近一次时钟滴答 越早访问 淘汰的越快
(d)第二次机会算法将置换哪个页面?
2 看载入时间 相当于二次FIFO 1203 3120 0312
37.假设有两个进程A和B,共享一个不在内存的页。如果进程A在共享页发生缺页,当该页读入内存时,A的页表项必须更新。
(a)在什么条件下,即使进程A的缺页中断处理会将共享页装入内存,B的页表更新也会延迟?
B不使用共享页或者发生中断 被算法交换出去
(b)延迟页表更新会有什么潜在开销?
进一步导致更多缺页
48.你能想象在哪些情况下支持虚拟内存是个坏想法吗?不支持虚拟内存能得到什么好处呢?请解释。
计算机想要知道全部的程序与数据, 可以一次性载入
好处:程序代码方便 就像汇编一样
49.虚拟内存提供了进程隔离机制。如果允许两个操作系统同时运行,在内存管理上会有什么麻烦?如何解决这些困难
根本上问题: 无法性能上接近, 就像虚拟机里的系统有自己内存
麻烦 难以在OS之间进行转换与处理TLB
比如你想给TLB表项 给每个内核确保他们进行VM的上下文交换,但有时候硬件处理Miss起来和你想做的不一样 因此你要在软件中处理TLB miss 并且提供上下文交换的ID使用硬件提供支持
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)