
上期回顾: 【操作系统】调度与死锁
个人主页:GUIQU.
归属专栏:操作系统

正文
1. 内存管理基础
1.1 内存管理的概念与目标
内存管理是操作系统中负责对计算机内存进行分配、回收、保护和扩充等操作的关键功能模块。其主要目标是高效地利用内存空间,确保多个进程能够安全且公平地共享内存资源,同时为进程提供一个统一的、抽象的内存访问视图,使得进程无需关心物理内存的实际细节,并且在内存资源紧张时能够借助虚拟内存等技术实现内存的有效扩充。例如,在多任务操作系统中,多个应用程序同时运行,内存管理系统需要合理分配内存给每个进程,防止进程之间相互干扰,并且在进程结束后及时回收内存以便重新分配。
1.2 内存空间的划分
1.2.1 系统区
系统区是内存中专门用于存放操作系统内核代码、数据结构以及一些关键的系统表项等信息的区域。这部分内存空间在系统启动时就被预留和初始化,并且在系统运行过程中通常是受保护的,不允许普通用户进程随意访问。例如,操作系统的进程调度程序、中断处理程序以及内存管理自身的数据结构如页表等都存放在系统区。
1.2.2 用户区
用户区则是可供应用程序使用的内存空间。当一个进程被创建时,操作系统会在用户区为其分配一定的内存空间,用于存放该进程的代码、数据、堆栈等信息。不同进程的用户区是相互独立的,这就保证了进程之间的内存隔离,一个进程无法直接访问另一个进程的用户区内存,从而提高了系统的安全性和稳定性。例如,一个文字处理程序和一个音乐播放程序在运行时,它们各自的代码和数据都存放在自己的用户区内存中。
2. 内存分配方式
2.1 连续分配方式
2.1.1 单一连续分配
单一连续分配是一种较为简单的内存分配方式,它将内存划分为两个区域:系统区和用户区,且用户区只分配给一个进程使用。这种分配方式的优点是简单易行,操作系统管理成本低;缺点是内存利用率低,因为无论进程实际需要多少内存,用户区都被单个进程独占,并且缺乏灵活性,不利于多任务处理。例如,早期的单用户操作系统如 DOS 系统在某些简单应用场景下可能采用这种分配方式。
2.1.2 固定分区分配
固定分区分配是预先将内存划分为若干个固定大小的分区,每个分区可以容纳一个进程。系统维护一个分区表,记录每个分区的起始地址、大小以及是否已被占用等信息。当有进程请求内存时,系统根据进程的大小在分区表中查找合适的空闲分区进行分配。这种方式相对单一连续分配提高了内存利用率,但由于分区大小固定,可能会出现内部碎片,即分区内未被进程使用的空闲空间。例如,如果有一个 10MB 的分区,而进程只需要 6MB,那么剩余的 4MB 就成为了内部碎片。
2.1.3 动态分区分配
动态分区分配根据进程的实际需求动态地划分内存空间。当进程创建时,系统从空闲内存空间中划分出一块大小合适的区域分配给该进程;当进程结束时,系统回收该进程所占用的内存空间,并将其重新加入到空闲内存列表中。这种方式有效地减少了内部碎片,但可能会产生外部碎片,即由于进程的不断创建和回收,内存空间被分割成许多大小不一的空闲块,难以满足较大进程的内存需求。例如,系统中有多个小的空闲块,但没有一个单独的空闲块能够满足一个大型游戏进程的内存要求。
2.2 离散分配方式
2.2.1 分页存储管理
分页存储管理将内存空间和进程的地址空间都划分成固定大小的页。内存以页为单位进行分配,进程的逻辑地址也被划分成页号和页内偏移量两部分。系统通过页表来实现逻辑地址到物理地址的映射,页表中记录了每个逻辑页对应的物理页框号。这种方式有效地解决了外部碎片问题,提高了内存利用率,但由于页表的存在,增加了内存访问的开销,因为每次访问内存都需要先查询页表。例如,在一个 32 位的操作系统中,如果页大小为 4KB,那么逻辑地址空间被划分为 2^20 个页,页表需要记录每个页的映射关系。
2.2.2 分段存储管理
分段存储管理根据进程中的逻辑段(如代码段、数据段、堆栈段等)来划分内存。每个段有自己的段名、段长和段基址等信息,系统通过段表来实现逻辑地址到物理地址的转换。这种方式便于程序和数据的共享与保护,因为不同进程中的相同逻辑段可以共享同一个物理段,并且可以对不同段设置不同的访问权限。例如,多个进程可能共享同一个操作系统的代码段,通过段表中的权限设置可以确保进程只能执行而不能修改该代码段。
2.2.3 段页式存储管理
段页式存储管理结合了分段和分页的优点。它先将进程按逻辑段划分,然后每个段再进一步划分成页,内存也以页为单位进行分配。系统中既有段表又有页表,通过段表找到段对应的页表,再通过页表实现页到物理页框的映射。这种方式综合了分段的逻辑清晰性和分页的内存利用率高的优点,但同时也增加了系统的复杂性和地址转换的开销。例如,在一个大型企业级应用中,程序的代码段、数据段等先分段,然后每段内再分页,这样既能方便管理和共享,又能高效利用内存。
3. 虚拟内存管理
3.1 虚拟内存的概念与原理
虚拟内存是一种基于计算机系统的存储体系结构,它使得进程可以使用比实际物理内存更大的地址空间。其原理是通过将部分暂时不使用的内存页面置换到磁盘等外部存储设备上,从而在逻辑上扩充了内存容量。当进程访问到已被置换到磁盘上的页面时,系统会将该页面重新调入内存,这个过程对进程是透明的。例如,一个计算机物理内存只有 4GB,但通过虚拟内存技术,一个大型数据库应用程序可以使用高达 16GB 的地址空间,系统会自动将不常用的页面置换到磁盘上,保证程序的正常运行。
3.2 页面置换算法
3.2.1 最佳置换算法(OPT)
最佳置换算法是一种理论上的最优算法,它选择在未来最长时间内不会被访问的页面进行置换。这种算法能够保证最低的缺页率,但由于它需要预知未来的页面访问序列,在实际系统中是无法实现的。例如,如果知道一个进程在接下来的 10 次页面访问中,某个页面在第 8 次访问后才会再次被访问,而其他页面在更短时间内会被访问,那么最佳置换算法就会选择在第 8 次访问后才需要的页面进行置换。
3.2.2 先进先出置换算法(FIFO)
先进先出置换算法按照页面进入内存的先后顺序进行置换,先进入内存的页面先被置换出去。这种算法简单易实现,但可能会出现 Belady 异常,即随着分配给进程的物理页面数增加,缺页次数反而增加。例如,有三个页面 1、2、3 依次进入内存,然后页面 4 进入时,按照 FIFO 算法,页面 1 被置换出去,但可能在后续的访问中,页面 1 又很快需要被访问,导致缺页。
3.2.3 最近最久未使用置换算法(LRU)
最近最久未使用置换算法选择最近一段时间内最久未被使用的页面进行置换。它基于程序的局部性原理,认为最近未被使用的页面在未来短期内也不太可能被使用。这种算法性能较好,但实现相对复杂,需要记录页面的访问时间或使用相关的数据结构来模拟访问时间。例如,在一个多任务处理的系统中,某个进程一段时间内频繁访问页面 A、B、C,然后长时间未访问页面 D,当需要置换页面时,LRU 算法会选择页面 D 进行置换。
3.2.4 时钟置换算法(CLOCK)
时钟置换算法是一种近似于 LRU 的算法,它通过一个环形指针来扫描页面,将最近未被访问的页面置换出去。每个页面有一个访问位,当页面被访问时,访问位被置为 1。算法扫描时,如果遇到访问位为 0 的页面,则将其置换出去;如果遇到访问位为 1 的页面,则将访问位清 0,继续扫描。这种算法相对简单,性能也较为可观,在实际系统中应用较广。例如,在一个操作系统中,内存中有多个页面,通过时钟置换算法的环形指针依次检查页面的访问位,决定是否置换页面。
4. 内存保护与共享
4.1 内存保护机制
4.1.1 界限寄存器
界限寄存器是一种简单的内存保护方式,它包括基址寄存器和限长寄存器。基址寄存器存放进程内存空间的起始地址,限长寄存器存放进程内存空间的长度。在进程访问内存时,硬件会检查访问地址是否在基址寄存器和基址寄存器加上限长寄存器所确定的范围内,如果超出范围,则产生越界中断,阻止进程的非法访问。例如,一个进程的基址寄存器值为 1000,限长寄存器值为 2000,那么该进程只能访问地址在 1000 到 3000 之间的内存空间。
4.1.2 存储保护键
存储保护键是另一种内存保护手段,每个内存块被赋予一个保护键,同时每个进程也有一个相应的保护键值。当进程访问内存时,只有当进程的保护键值与内存块的保护键匹配并且具有相应的访问权限(如读、写、执行等)时,访问才被允许,否则产生保护异常。例如,操作系统内核所在的内存块保护键为 0,只有具有保护键值为 0 且有相应权限的进程(如系统进程)才能访问该内存块。
4.2 内存共享方式
4.2.1 代码共享
代码共享是内存共享的一种常见形式,多个进程可以共享同一段只读的代码,如操作系统的内核代码、库函数代码等。通过共享代码,可以减少内存的占用,提高内存利用率。例如,多个应用程序在运行时都需要调用标准库中的 printf 函数,这些程序可以共享 printf 函数所在的代码段,而无需各自复制一份。
4.2.2 数据共享
数据共享相对复杂一些,需要考虑数据的一致性和同步问题。在某些情况下,多个进程可以共享一些全局数据结构或文件数据等。例如,多个进程可能共同访问一个数据库文件,在这种情况下,需要通过文件锁、信号量等同步机制来确保不同进程对共享数据的正确访问,防止数据冲突和不一致。
结语
感谢您的阅读!期待您的一键三连!欢迎指正!

5万+

被折叠的 条评论
为什么被折叠?



