内存管理的层次
内存管理的目标
- 抽象:
- 逻辑地址空间(不考虑底层细节,只需访问一个连续的地址空间)
- 保护:
- 独立地址空间(不会被别的进程破坏)
- 共享
- 访问相同内存(进程间数据传递)
- 虚拟化
- 更多的地址空间(把最需要的数据放入内存,暂时不需要的数据放到磁盘)
地址空间 & 地址生成
- 物理地址空间
- 逻辑地址空间
地址空间定义:
物理地址空间:主存和磁盘等硬件直接管理的地址空间
逻辑地址空间:运行的应用程序直接看到一维的线性地址空间
操作系统将逻辑地址空间映射到实际的地址空间
逻辑地址空间的生成:
c语言–编译–>汇编语言–汇编–>机器语言(.o文件)–链接–>可执行文件(.exe)–载入(loader)/程序重定位–>加载到内存中去运行
c/汇编语言内的变量名和函数名实际就是地址,是名字
机器语言内已将变量符号名和函数符号名等转化为相应的相对连续的地址空间(逻辑地址空间),是地址的二进制
逻辑地址和物理地址的对应:
CPU的内存管理单元(MMU)中有物理地址和逻辑地址的对应关系表,当CPU要取指或数据是则根据逻辑地址查表前往对应的物理地址去取出
操作系统负责建立物理地址空间和逻辑地址空间的映射关系(建表),存在内存中,为了加快访问放在CPU中进行缓存。
连续内存分配:
内存碎片:
空闲内存不能被利用
- 外部碎片:在分配单元之间的未使用的内存
- 内部碎片:在分配单元内未使用的内存
分区的动态分配:
- 将应用程序加载到内存时,需要为应用程序分配内存空间
- 应用程序在运行时,需要访问数据,要给这些数据分配内存空间
分配策略:
- 首次适配:简单,产生外部碎片,大的内存块集中在内存尾部
- 最佳适配:重分配慢,和应用程序所需内存差值最小的内存块,产生小的外部碎片,不易于后续的回收
- 最差适配:重分配慢,寻找和所需内存差值最大的内存块,对大块的请求造成影响
减少内存碎片的方法:
压缩式碎片整理
交换式碎片整理(换入换出)
- 充分应用硬盘,抢占正在等待的程序,将等待的程序载入硬盘上去,并回收他们的内存。
伙伴系统:
- 整个可分配的分区大小 2^U
- 需要的分区大小为 2^(U-1) < s <= 2^U 时,把整个块分配给该进程
- 如 s <= 2^(i-1) ,将大小为 2i 的当前空闲分区划分成两个大小为 2^(i-1) 的空闲分区
- 重复划分过程,直到 2^(i-1) < s <= 2^i ,并把一个空闲分区分配给该进程
伙伴系统的实现
- 数据结构
- 空闲块按大小和起始地址组织成二维数组
- 初始状态:只有一个大小为 2^U 的空闲块
- 分配过程
- 由小到大在空闲块数组中找最小的可用空闲块
- 如空闲块过大,对可用空闲块进行二等分,知道得到合适的可用空闲块
- 释放过程
- 把释放的块放入空闲块数组
- 合并满足合并条件的空闲块
- 合并条件
- 大小相同 2^i
- 地址相邻
- 起始地址较小的块的起始地址必须是 2^(i+1) 的倍数
非连续的内存分配:
非连续内存分配方法:
- 一个程序的物理地址空间是非连续的
- 更好的内存利用和利用,减少内碎片和外碎片
- 允许共享代码和数据(共享库等…)
- 支持动态加载和动态链接
分段
将程序的不同段分散到多个物理地址中去
-
连续的逻辑地址映射到不连续的物理地址中去
-
例如c程序中栈段,堆段,代码段,数据段
寻址方案的实现:
段访问机制:
- 段号
- 查询段表查找所对应物理内存的起始地址
- 通过段表还可以查询到段的长度限制
- 段内偏移
- 段偏移和段长度进行比较,若偏移超出段长,则该逻辑地址无效,抛出异常
PS:分段机制类似之前的动态分区,也会产生外部碎片,但是因为程序分布在不连续的物理地址空间,所以产生的外部碎片大大减小了。
分页
分页分为页寻址和页帧寻址两部分
- 页帧(物理地址的寻址)
- 页(逻辑地址的寻址)
分页寻址机制:
- 页映射到帧
- 页是连续的虚拟内存
- 帧是非连续的物理内存
- 不是所有的页都有对应的帧
根据逻辑地址获取页号p–>查询页表根据页号查到帧号f—>f和逻辑地址中的偏移量o结合为物理地址–>[2^(o的位数,例如图中为9)*f+o] 即为所需要数据所在的物理地址
PS:采用分页机制类似之前的固定分区,但分页技术的分区相当小,一个程序可以占据不连续的多个分区,因而大大减小了内部碎片,提高了内存利用率。
页表
- 每个进程有一个页表
- 页表岁进程运行状态的变化而动态变化
- 页表基址寄存器存储页表在内存中的地址
页存储管理机制存在性能问题:
- 访问一个内存单元需要访问2次内存(访问页表项,访问数据)
- 页表可能非常大
解决方法:
- 建立快表(TLB)
- 多级页表
- 反向页表