内存管理
背景
- 创建进程首先要将程序和数据装入内存。将用户源程序变为可在内存中执行的程序,通常需要编译、链接、装入三个步骤
- 可执行程序经过编译产生了逻辑地址,为了提高进程的并发和内存利用 率,逻辑地址和运行物理地址间需要地址转换。程序装载进内存在运行时重定位,重定位就是物理地址到逻辑地址的一个过程。
- 嵌入式操作系统多数内存管理非常简单,甚至没有内存管理。
- PC和服务器、分布式等现代操作系统基本都支持虚拟内存管理。
内存分配和管理方案
连续内存分配
固定分区–等长分区
- 操作系统初始化时将内存等分成k个分区
bool AvailSec[k]; int SecSize;
内存请求算法 //进程创建时
1. if(reqSize>SecSize) exit
2. 找出AvailSec[i]为真的i
3. 如果有,返回分区i的基址
4. 否则,将current加入请求队列等待
固定分区–变长分区
- 初始化时将内存分成k个大小不同的分区
struct Section AvailSec[k];
内存请求算法 //进程创建时
1. if(reqSize>MaxSize) exit
2. 找出AvailSec[i].Size>reqSize且
AvailSec[i].Size最小的空闲分区i
3. 如果有,返回分区i的基址
4. 否则,将current加入请求队列等待
可变分区
根据reqSize(请求大小)进行动态分割
内存请求算法 //进程创建时
1. if(reqSize>内存大小)exit
2. if(reqSize>空闲空间总尺寸)
将current加入请求队列等待
3. 从空闲分区划出一个reqSize,并返回
其基地址 //那个空闲分区
4. 修改分区数据结
可变分区数据结构
新的内存请求:reqSize = 100K
进程2执行完毕,释放内存
分区分配算法
- 首次适配:分配首次找到的满足要求的空闲分区。特点:快
- 最佳适配:查找最小的满足要求的空闲分区。特点:
- 1)搜索整个空闲分区,慢
- 2)会产生许多小的空闲分区
- 最差适配:查找最大的满足要求的空闲分区。
- 1)搜索整个空闲分区,慢
- 2)新产生的空闲分区大一些。
碎片问题
- 碎片:内存中剩余的无法使用的存储空间
- 外部碎片:随着进程不断的装入和移出,对分区不断地分割,使得内存中产生许多特别小的分区,它们并不连续可用
- 内部碎片:对固定分区来说,被分配给某个进程的分区中未被占用的空间,是无法分给其他进程的
- 碎片问题可以消除(内存紧缩),但时间成本太高
外部碎片处理–内存紧缩
将空闲分区合并在一起,需要移动进程(复制内容)
但内存紧缩需要花费大量时间,连续分配技术并不合适。
分段内存管理
程序有主程序、变量集、函数库、动态数组、栈等部分,不同部分有各自的用途,可以考虑不同部分分为不同的段。
但这样一来一个进程需要记录多个基址,且仍需要内存分区表和内存分配算法
分段地址翻译
分段地址由<段号,段内偏移>确定,其中:
段号
=
I
N
T
[
逻辑地址
/
逻辑段大小
]
偏移
=
[
逻辑地址
]
%
逻辑段大小
段号=INT[逻辑地址/逻辑段大小]\\ 偏移=[逻辑地址]\%逻辑段大小
段号=INT[逻辑地址/逻辑段大小]偏移=[逻辑地址]%逻辑段大小
分段技术总结
- 将程序按含义分成若干部分,即分段
- ld从0开始编址每个段(链接速度会很快):汇编时从0编址、链接时可不为0
- 创建进程(分别载入各个段)时,建立进程段表
- 内存仍用可变分区进行管理,载入段时需调分配算法
- PC及数据地址要通过段表算出物理地址,到达内存
- 进程切换时,进程段表也跟着切换
分段技术优缺点
优点:
- 不同的段有不同含义,可区分对待
- 每个段独立编址,编址容易
缺点:
- 空间预留;空闲空间很大却不能分配;内存紧缩
分页内存管理
将内存分为小块的页,一次只分配给一点(没有外部碎片,内部碎片有上限)。
页表
- 页的逻辑地址由页号和页内偏移组成,其中页号的范围取决于页的数量,页内偏移的范围取决于页面的尺寸。
- 进程页表中记录着页号与页框号的映射关系,当需要查找某个页时需要去进程页表中查找该页所在的页框号,再去对应页框号查找到该页。
分页地址翻译
- PCB中记录着该进程对应的页表指针
- 逻辑地址翻译为物理地址,用页号去页表查找页框号,页框号和偏移组成物理地址。
一些细节问题
- 和段表不一样,页表可能会很大
- 页是用来解决碎片问题的=>页面尺寸应尽量小
- 页面尺寸通常为4K,32位机器有 2 20 2^{20} 220个页面,因为低12位是偏移
- 每条指令都要查几次页表,因此查表效率很重要
页表项数据结构
Page Table Entry(PTE)
unsigned translate(unsigned va, int wr){
struct pte *pte = &page_table[va >> 12]; // 页号
if(!pte->valid || (wr && !pte->writeable))
throw address_fault;
return (pte->ppn << 12) | (va & 0xfff);
}
习题
例1:已知某分页系统,主存容量为64k,页面大小为1k,对一个4页大的 作业,第0、1、2、3页被分配到内存的2、4、6、7块中。求:将十进制的 逻辑地址1023、2500、4500转换成物理地址。
- 1023/1K,得到页号为0,页内地址1023。 又 对应的物理块号为2,故物理地址为2*1k+1023=3071
- 2500/1K,得到页号为2,页内地址452 又 对应的物理块号为6,故物理地址为6*1k+452=6596
- 4500/1K,得到页号为4,页内地址404 因为页号不小于页表长度,故产生越界中断
设有8页的逻辑空间,每页有1024字节,它们被映射到32块的物理存储区 中,那么逻辑地址的有效位是_位,物理地址至少是__位。
- 逻辑地址有两个部分组成:页号和页内偏移地址。逻辑空间有8 ( 2 3 2^3 23)页, 说明页号需要3个二进制位描述,而每页有1024( 2 20 2^{20} 220)字节,说明页内偏 移地址为10二进制位描述,因此逻辑地址的有效位为3+10=13位。
- 因为物理地址与逻辑地址的页面大小相同,而物理存储块为32( 2 5 2^5 25)占5 位,所以物理地址至少为5+10=15位
多级页表
- 2 20 2^{20} 220个页表项,每个4字节,都放在内存,要4M内存
- 实际上大部分逻辑地址根本不会用到
- 引入多级页表,顶层页表常驻内存,不需要映射的逻辑地址不需要建立页表项
多级页表地址翻译
TLB
- 多级页表地址翻译效率很低,要提高效率
- TLB(Translation Look-aside Buffer)是一组关联快速寄存器组
采用TLB后的地址翻译
TLB命中时效率会很高,未命中效率会降低,平均后仍表现良好。
有效访问时间
=
H
i
t
R
×
(
T
L
B
+
M
A
)
+
(
1
−
H
i
t
R
)
×
(
T
L
B
+
2
M
A
)
有效访问时间 = HitR\times(TLB+MA)+(1-HitR)\times(TLB+2MA)
有效访问时间=HitR×(TLB+MA)+(1−HitR)×(TLB+2MA)
其中
H
i
t
R
HitR
HitR是命中率,TLB是访问TLB时间,MA是内存访问时间。
因此TLB要发挥作用,命中率应尽可能高,即TLB越大越好,但TLB价格昂贵,通常条目数在[64, 1024],根据局部性,64就能发挥作用了。
TLB动态变化
- 如果TLB未命中,可将查到的页表项载入TLB
- 如果TLB满了,需要选择一个条目来替换
- 有时候希望某些条目能够固定下来(如内核代码),某些TLB的设计有这样的功能
- 进程切换以后,所有的TLB表项都变为无效
- 如果进程马上又切换回来,则这种策略会很低效,有的TLB设计中条目项保存ASID(Address-space identifer)
分页技术总结
实现机理
- 逻辑地址空间和内存都分割大小相等的片(页和页框)
- 每个进程用页表(多级、反向等)建立页和页框的映射
- 进程创建时申请页,可用表、位图等结构管理空闲页
- 逻辑地址通过页表算出物理地址,到达内存
- 进程切换时,页表跟着切换
优点
- 靠近硬件,结构严格,高效使用内存
缺点
- 不符合程序员思考习惯
段、页结合
段面向用户,页面向硬件
实现机理
- 程序的段划分的是线性地址空间
- 线性地址空间和内存被分割大小相等的片(页和页框)
- 进程用页表建立页和页框的映射
- 进程创建申请段(线性地址空间),段申请页(物理内存)
- 逻辑地址通过段表加页表算出物理地址,到达内存
- 进程切换时,段表和页表都跟着切换
优点
- 符合程序员习惯,并可高效利用内存
缺点
- 复杂,访问一次地址需要查表多次
进行一次地址翻译需要:
- 找到段表;2) 查段表; 3)找TLB;4)找到页目录表;5)查找页目录项;6)找到页表;7)查找页表;8)形成物理地址;9)需要段越界检查;10)需要进行段保护权限检查;11)需要进行页保护权限检查…
习题
多进程在主存中彼此互不干扰的环境下运行,操作系统是通过(B) 来实现的。
A.内存分配 B.内存保护 C.内存扩充 D.地址映射
解析:多进程的执行通过内存保护实现互不干扰,如页式管理中有页地址越界保护,段式管理中有段地址越界保护
- 下列关于页式存储的论述中,正确的是(A)。
I.在页式存储管理中,若关闭 TLB,则每当访问一条指令或存取 一个操作数时都要访问 2 次内存
Ⅱ.页式存储管理不会产生内部碎片
Ⅲ.页式存储管理中的页面是为用户所感知的
Ⅳ.页式存储方式可以采用静态重定位
A. 仅 I B.I、Ⅳ C:I、Ⅱ、Ⅳ D.全都正确
解析: I 正确:关闭 TLB 后,每当访问一条指令或存取一个操作数时都要先 访问页表(内存中),得到物理地址后,再访问一次内存进行相应操 作。 Ⅱ错误:凡是分区固定的都会产生内部碎片,而无外部碎片。 Ⅲ错误:页式存储管理对于用户是透明的。 Ⅳ错误:静态重定位是在程序运行之前由装配程序完成的,必须分配 其要求的全部连续内存空间。而页式存储管理方案是将程序离散地分 成若干页(块),从而可以将程序装入不连续的内存空间,显然静态 重定位不能满足其要求。
某操作系统采用段式管理,用户区主存为 512KB,空闲块链入空 块表,分配时截取空块的前半部分(小地址部分).初始时全部空 闲。执行申请、释放操作序列 reg(300KB),reg(100KB),release (300KB),reg(150KB),reg(50KB),reg(90KB):
1.采用最先适配,空块表中有哪些空块?(指出大小及始址)
最先适配的内存分配情况如下图中的(a)所示。
内存中的空块为: 第一块:始址 290K,大小 10KB;第二块:始址 400K,大小 112KB。
(过程就是一开始300KB存在0-300KB,然后100KB存在300-400KB,然后释放300KB,150KB存在0-150KB,50KB存在150-200KB,90KB存在200-290KB)
2.采用最佳适配,空块表中有哪些空块?(指出大小及始址)
最佳适配的内存分配情况如下图中的 b)所示。
内存中的空块为: 第一块:始址 240K,大小 60KB;第二块:超始地址 450K,大小 62KB。
3.若随后又要申请 80KB,针对上述两种状况会产生什么后果?
若随后又要申请 80KB,则最先适配算法可以分配成功,而最佳适配算 法则没有足够大的空闲区分配。