- 某次面试被问到 一时没反应过来要回答连续与非连续再细分为分区,分页,分段,段页式 这里记录一下
连续分配
特点
连续分配要求为进程分配一块连续的内存空间,内存分配和回收相对简单,但容易产生内存碎片,降低内存利用率具体类型
- 单一连续分配
- 内存划分:将整个物理内存分为两个连续区域:
- 系统区:存放操作系统内核代码和数据(如中断处理程序、进程管理模块等),通常位于内存低地址(如0地址开始)或高地址。
- 用户区:整个剩余内存作为一个连续的区域,同一时间只分配给一个用户进程使用。
- 分配逻辑:进程运行时,整个用户区全部分配给它;进程结束后,释放用户区供下一个进程使用。
- 特点:
- 单任务特性:同一时间只能有一个用户进程在内存中运行(操作系统常驻内存)。
- 地址转换简单:通过重定位寄存器(基址寄存器)实现逻辑地址到物理地址的转换。例如,进程逻辑地址为 L,物理地址 = 重定位寄存器值 + L(确保不越界访问系统区)。
- 无碎片问题:用户区作为整体分配,没有内碎片或外碎片(但进程大小若小于用户区,会浪费部分空间,可视为一种“隐性内碎片”)。
- 优点:
- 实现最简单,无需复杂的内存管理算法(只需标记用户区是否空闲)。
- 地址转换速度快(仅需一次加法运算)。
- 缺点:
- 资源利用率极低:内存中只能有一个用户进程,CPU和内存常处于空闲状态。
- 不支持多任务:无法同时运行多个进程,严重限制系统吞吐量。
- 内存浪费:若进程大小远小于用户区,剩余空间无法利用(例如用户区1GB,进程仅需100MB,浪费900MB)。
- 固定分区分配
- 预先划分分区:操作系统初始化时,将用户区内存划分为多个固定大小的连续分区(分区大小可相同或不同),划分后分区大小和数量不再改变。
- 分区管理:通过“分区表”记录每个分区的信息:起始地址、大小、状态(空闲/已分配)。
- 分配逻辑:当进程请求内存时,从分区表中找一个大小大于等于进程需求且状态为空闲的分区,分配给进程(一个分区只能装一个进程);进程结束后,将分区标记为空闲。
- 特点
- 多任务支持:可同时运行多个进程(最多等于分区数量)。
- 分区大小设计:
- 等大小分区:所有分区尺寸相同(如4个1GB分区),适合运行大小相近的进程。
- 不等大小分区:分区尺寸不同(如1个4GB、2个2GB、4个1GB),适配不同规模的进程(大型、中型、小型)。
- 内部碎片问题:若进程大小小于分区大小,分区内未使用的空间称为“内碎片”(例如512MB分区装入300MB进程,产生212MB内碎片)。
- 优点:
- 实现简单:分区表管理成本低,分配/回收仅需修改分区状态。
- 支持多任务:相比单一连续分配,提高了CPU和内存利用率。
- 缺点:
- 内碎片浪费:进程大小通常小于分区,导致内存利用率不高(不等大小分区可缓解但无法消除)。
- 灵活性差:分区大小固定,若进程大于所有分区则无法分配(“内存溢出”);若进程远小于分区,浪费严重。
- 分区数量固定:能同时运行的进程数量受限(例如8个分区最多同时运行8个进程)。
- 动态分区分配
- 按需动态划分:不预先划分分区,而是在进程加载时,根据其实际内存需求,从空闲内存中“切割”出一块大小恰好合适的连续区域分配给进程(分区大小 = 进程需求)。
- 空闲分区管理:通过“空闲分区链”(链表)记录所有空闲区域的起始地址和大小(空闲分区之间可能不连续)。
- 分配与回收:
- 分配:从空闲链中找合适的分区,切割后分配给进程,剩余部分仍作为空闲分区保留在链中。
- 回收:进程结束后,释放的分区重新加入空闲链,若与相邻空闲分区连续,则合并为一个大分区(“分区合并”,减少碎片)。
- 特点
- 分区大小动态变化:分区大小完全由进程需求决定,无固定尺寸。
- 分配算法:需通过算法从空闲链中选择分区,常见算法包括:
- 首次适应:从链头开始,选第一个能容纳进程的空闲分区。
- 最佳适应:遍历所有空闲分区,选最小的、能容纳进程的分区(减少内碎片)。
- 最坏适应:选最大的空闲分区(剩余部分仍可能被利用)。
- 邻近适应:从上次分配位置开始查找,选第一个合适的分区。
- 外部碎片问题:频繁分配和回收后,空闲内存会被分割成许多分散的小分区(“外碎片”),总空闲内存足够但无法分配给需要连续空间的大进程。
- 优点:
- 内存利用率高:按需分配,几乎无内碎片(仅最后一次切割可能产生极小内碎片)。
- 灵活性好:支持任意大小的进程(只要总空闲内存足够),能动态适应进程需求。
- 支持多任务:无分区数量限制(理论上可运行任意多个进程,只要内存足够)。
- 缺点:
- 外碎片问题:长期运行后,大量小碎片导致“内存总量足够但无法分配”。
- 分配/回收效率低:需遍历空闲链查找合适分区,算法复杂度高于固定分区;回收时可能需要合并相邻分区,进一步增加开销。
- 需支持动态重定位:进程内存地址可能不连续,需硬件(如基址-限长寄存器)支持地址转换。
非连续分配
特点
非连续分配将内存空间划分为多个不连续的区域,每个区域可以分配给一个进程,进程在内存中占据不连续的物理地址空间,内存利用率高,能有效减少内存碎片,避免了内存碎片问题,但需要更复杂的地址转换机制具体类型
- 分页存储管理
将进程的逻辑地址空间和物理内存空间均划分为大小相等的“块”(逻辑上称为“页”,物理上称为“页框”),通过“页表”建立页与页框的映射,使进程的不同页可以分散存储在物理内存的不同页框中(无需连续)。
- 页:进程逻辑地址空间的基本单位,大小固定(如4KB、8KB,由系统决定)。
- 页框:物理内存的基本单位,大小与页相同(保证一页恰好放入一个页框)。
- 页表:每个进程一张,记录逻辑页号到物理页框号的映射关系(还可能包含权限位、有效位等控制信息)。
- 地址结构:逻辑地址 = 页号(高位) + 页内偏移量(低位)。
- 地址转换过程
- 进程访问逻辑地址时,CPU自动拆分出页号和页内偏移量。
- 用页号查页表,得到对应的物理页框号。
- 物理地址 = 物理页框号 × 页大小 + 页内偏移量。
- 优点:
- 彻底消除外碎片(物理内存按页框划分,分配粒度固定)。
- 内存利用率高(进程无需连续空间,小页框可适配不同大小的进程)。
- 分配/回收简单(按页框操作,无需考虑连续性)。
- 缺点:
- 存在少量内碎片(进程最后一页未装满的部分)。
- 页表可能过大(例如32位系统,4KB页大小,页表需1M个条目,占用4MB内存)。
- 按页划分不符合程序逻辑(页可能割裂代码、数据等逻辑单元,不便于共享和保护)。
- 分段存储管理
按进程的逻辑结构(如代码段、数据段、堆段、栈段)将逻辑地址空间划分为若干个“段”(每个段有独立的逻辑意义),每个段的大小不固定(由内容决定),通过“段表”建立段与物理内存的映射,段内连续、段间离散存储。
段:进程逻辑地址空间的基本单位,大小不固定(如代码段可能100KB,栈段可能20KB),每个段有唯一的段号。
段表:每个进程一张,记录段号对应的物理内存起始地址(段基址)和段的长度(段限长)。
地址结构:逻辑地址 = 段号(高位) + 段内偏移量(低位),段内偏移量不能超过段长(否则越界)。
地址转换过程
- 进程访问逻辑地址时,拆分出段号和段内偏移量。
- 用段号查段表,得到段基址和段限长,检查偏移量是否超过段限长(防止越界)。
- 物理地址 = 段基址 + 段内偏移量
优点:
- 符合程序逻辑(按功能划分段,便于理解和维护)。
- 便于共享和保护(例如多个进程可共享代码段,对数据段设置只读/读写权限)。
- 无内碎片(段大小由内容决定,无需预留空间)。
缺点:
- 存在外碎片(段间分散的空闲空间,难以利用)。
- 段大小可变,分配/回收复杂(需匹配大小合适的连续空闲区)。
- 段表管理成本高(段数量可能多,且段大小差异大)。
- 段页式存储管理
结合分页和分段的优点:先将进程按逻辑结构分段,再将每个段分页。既保留分段的逻辑清晰性,又利用分页解决外碎片问题。
- 段:进程按逻辑结构划分(如代码段、数据段),每个段有段号。
- 页:每个段再划分为大小固定的页(与物理页框大小一致)。
- 段表和页表:
- 段表:每个段对应一个页表,段表项记录该段的页表起始地址和页表长度。
- 页表:每个页表记录该段内页号到物理页框号的映射。
- 地址结构:逻辑地址 = 段号 + 页号 + 页内偏移量(三级结构)。
- 地址转换过程
- 拆分逻辑地址为段号、页号、页内偏移量。
- 用段号查段表,得到该段的页表基址和页表长度(检查页号是否越界)。
- 用页号查该段的页表,得到物理页框号。
- 物理地址 = 物理页框号 × 页大小 + 页内偏移量。
- 优点:
- 兼顾逻辑清晰性(分段)和内存高效利用(分页)。
- 消除外碎片(分页机制),内碎片少(仅页内少量剩余)。
- 便于共享和保护(按段管理权限,按页分配内存)。
- 缺点:
- 地址转换复杂(需查段表和页表两级映射),速度较慢(通常需TLB硬件缓存加速)。
- 管理成本高(段表和页表均需占用内存)。