OS-内存管理
内存
- 内存用于存放数据。程序执行前先放到内存中才能被CPU处理,缓和CPU和外存之间的速度矛盾。
- 内存地址从0开始,每一个地址对应一个存储单元。如果计算机按字节编址,每个存储单元大小为1字节,即8个二进制位。如果计算机按字编址,则每个存储单元大小为一个字(位数取决于字长)
- \(2^{10} = 1K ,2^{20} = 1M , 2^{30} = 1G\)
不同的存储管理模式
单一连续区存储管理
- 内存被划分为两个固定大小的区域:
- 系统区:仅供操作系统使用,通常位于内存的低地址部分。
- 用户区:整个剩余的内存空间,全部分配给一个正在运行的应用程序。
- 任何时候,内存中最多只有一个用户程序。
- 通常用于单道程序设计环境(单任务系统)中,系统只能同时运行一道程序。管理简单,内存利用率低。有内部碎片,无外部碎片。

固定式分区存储管理
- 在系统启动时,操作系统将用户内存空间静态地划分为若干个大小固定的分区。每个分区的大小可以相等,也可以不等。
- 当一个作业到达时,系统根据它的大小,将它放入一个足够容纳它的最小空闲分区中。
- 会产生内部碎片(内零头):如果作业大小小于所在分区的大小,该分区内部剩余的空间就被浪费了。这是固定分区的主要缺点。

可变式分区存储管理
- 内存分区不是预先划定的,而是在作业装入时,根据作业的实际大小动态地创建分区。
- 没有内部碎片。
- 会产生外部碎片:随着作业的分配和回收,内存中会出现许多小的、不连续的空闲分区。虽然它们的总容量可能很大,但无法分配给任何一个新作业。这是可变分区的主要缺点。

可重定位分区存储管理
- 在可变分区管理的基础上,增加碎片压缩功能。
- 当内存中的外部碎片多到无法满足新作业的需求时,操作系统会暂停所有正在运行的程序,将已分配的分区向内存一端移动,从而将所有小的外部碎片合并成一个大的连续空闲区。
- 由于程序在内存中的位置发生了移动,所以必须动态重定位程序的地址。
- 紧凑过程开销巨大:需要移动大量数据,消耗CPU时间,系统性能会暂时下降。

空闲内存管理

使用位图的存储管理
- 存储空间被分为一系列分配单元,一个存储分配单元对应于位图中的一位。
- 0表示空闲,1表示占用。
- 分配时,查找位示图中对应位为0的存储块。回收时,将回收的存储块在位示图中所对应的位设置为0。
- 分配单元越小,位示图就越大,管理的开销也越大。
使用链表的存储管理
- 使用链表维护已分配内存段和空闲内存段。
- 链表的一个节点可以是一个进程,也可以是两个进程之间的空闲区。
- 节点结构包含:空闲/进程 指示位,起始地址,长度,下一个节点的指针。
- 节点可以按照地址排序。
内存分配算法
首次适应算法(First Fit)
- 从低地址,按地址顺序依次查找空闲内存区,直到找到第一个足够大的空闲区,若空闲区大小大于分配空间,则分为两个部分,一部分被分配,一部分空闲。
- 缺点:会产生外部碎片。
循环首次适应算法/下次适应算法(Next Fit)
- 算法逻辑与首次适应算法相同,但是每次查找到一个合适的空闲区之后记录此时的位置。下次寻找空闲区时从上次结束的地方开始查找,而不是像首次适应算法那样从头开始。
最佳适应算法(Best Fit)
- 搜索整个链表,找到能容纳进程的最小的空闲区。
- 缺点:会产生很多很小的空闲区。
最坏适应算法(Worst Fit)
- 总是分配最大的空闲区。
- 可以使得剩下的空闲区不至于太小。
快速适应算法(Quick Fit)
- 为常用大小的空闲区维护单独的链表。即将空闲块按大小分类到不同的链表中,分配时直接到对应大小的链表中查找,避免遍历所有空闲块。
内存回收算法

- 当一个进程结束时需要对其分配的内存进行回收,其相邻内存块一共有四种情况。每种情况需单独处理。
虚拟内存
传统存储管理方式的特点
- 一次性:作业必须一次性全部装入内存后才能开始运行。这会造成两个问题:作业很大时,不能全部装入内存,导致大作业无法运行;当大量作业要求运行时,由于内存无法容纳所有作业,因此只有少量作业能运行,导致多道程序并发度下降。
- 驻留性:一旦作业被装入内存,就会一直驻留在内存中,直至作业运行结束。事实上,在一个时间段内,只需要访问作业的一小部分数据即可正常运行,这就导致了内存中会驻留大量的、暂时用不到的数据,浪费了宝贵的内存资源。
局部性原理
- 时间局部性:如果执行了程序中的某条指令,那么不久后这条指令很有可能再次执行;如果某个数据被访问过,不久之后该数据很可能再次被访问。(因为程序中存在大量的循环)
- 空间局部性:一旦程序访问了某个存储单元,在不久之后,其附近的存储单元也很有可能被访问。(因为很多数据在内存中都是连续存放的,并且程序的指令也是顺序地在内存中存放的)
虚拟内存
-
基于局部性原理,在程序装入时,可以将程序中很快会用到的部分装入内存,暂时用不到的部分留在外存,就可以让程序开始执行。
-
在程序执行过程中,当所访问的信息不在内存时,由操作系统负责将所需信息从外存调入内存,然后继续执行程序。
-
若内存空间不够,由操作系统负责将内存中暂时用不到的信息换出到外存。
-
在操作系统的管理下,在用户看来似乎有一个比实际内存大得多的内存,这就是虚拟内存
-
虚拟内存技术的实现建立在离散分配内存管理方式的基础上,实现请求式存储管理
-
请求式存储管理:
- 请求调页:在程序执行过程中,当所访问的信息不在内存时,由操作系统负责将所需信息从外存调入内存,然后继续执行程序。
- 页面置换:若内存空间不够,由操作系统负责将内存中暂时用不到的信息换出到外存。
-
特征:
- 多次性:无需在作业运行时一次性全部装入内存,而是允许被分成多次调入内存。
- 对换性:在作业运行时无需一直常驻内存,而是允许在作业运行过程中,将作业换入、换出。
- 虚拟性:从逻辑上扩充了内存的容量,使用户看到的内存容量,远大于实际的容量。
地址重定位
- 当一个程序(比如一个C语言源文件)被编译后,它会生成一个目标文件。这个文件中的指令和数据的地址通常是从零开始的,我们称之为逻辑地址或相对地址。
- 然而,当这个程序要被加载到物理内存中运行时,它不可能总是从物理地址0开始。它必须被放入一块空闲的物理内存中,比如从物理地址
X开始。 - 地址重定位 就是把程序的逻辑地址转换为运行时所在的物理地址的过程。静态重定位和动态重定位是解决这个问题的两种不同策略。
静态重定位
- 静态重定位:将该程序涉及到的所有地址加上基地址(程序在内存中的起始地址),完成地址重定位。
- 静态重定位的地址变换需要在程序运行前完成。在程序加载时完成程序中所有地址项的变换。由操作系统的加载器 来完成。
- 缺点:
- 一旦加载,程序就必须占据一块连续的物理内存,且无法在内存中移动。
- 操作系统无法阻止程序访问其他程序的内存,因为程序使用的是绝对物理地址。
- 优点
- 实现简单,在程序执行时没有地址转换的开销。

- 实现简单,在程序执行时没有地址转换的开销。
动态重定位
- 动态重定位的地址变换就在程序运行时进行,在程序加载不需要对程序中的地址项进行地址变换。
- 当程序被调度运行时,操作系统会在一个特殊的CPU寄存器(通常是基址寄存器)中设置该程序的基地址(Base)。同时,可能还会有一个界限寄存器用于内存保护。
- 硬件地址转换: 在程序运行过程中,CPU每取一条指令或访问一个数据,发出的都是逻辑地址。内存管理单元(MMU) 这个硬件部件会自动地将这个逻辑地址与基址寄存器中的值相加,得到真正的物理地址,然后再用这个物理地址去访问内存。
- 物理地址 = 逻辑地址 + 基址寄存器
- 例如,指令
JMP 105被执行,如果基址寄存器中的值是500,那么MMU会计算105 + 500 = 605,然后去访问物理地址605。 - 缺点:
- 每次内存访问都需要一次地址加法操作,虽然由硬件完成非常快,但理论上仍存在极小的开销。
- 优点:
- 可以配合界限寄存器,确保程序不会访问到分配给它的内存区域之外,从而保护操作系统和其他程序。
- 程序可以在不重启的情况下,在物理内存中移动。

分页机制
基本思想
- 将物理内存划分为固定大小的、微小的块,称为 “页框” 或 “物理页”。
- 将程序的逻辑地址空间也划分为同样固定大小的块,称为 “页” 或 “虚拟页”。
- 程序的“页”可以存放在物理内存中任何一个可用的“页框”里。
- 通过一个“页表”来记录每个“页”被放到了哪个“页框”里。

分页流程
- 当一个程序被启动时,操作系统为它创建一个虚拟地址空间。这个地址空间是连续的,从0到一个最大值(如4GB)。操作系统将这个连续的虚拟地址空间切割成许多个页(例如,每个页大小为4KB)。同时,物理内存也被划分为同样大小的页框。
- 操作系统为每个进程创建一个名为 页表 的数据结构,建立 虚拟页 到 物理页框 的映射关系。
- 当CPU执行一条指令,需要访问一个内存地址时,这个地址
0x1234是一个虚拟地址。CPU必须通过内存管理单元(MMU) 将它转换成物理地址。

- 程序访问了一个“合法”但“未加载入内存”的虚拟地址时,硬件触发缺页异常。
缺页异常
-
MMU在地址转换过程中发现目标页的页表项有效位为0,会立即触发缺页异常。
-
然后操作系统需要找到一个空闲的物理页框来存放即将被加载的页面。如果系统有空闲页框:直接从空闲链表中取一个。 如果没有空闲页框:则必须执行页面置换算法(如LRU、Clock算法等),选择一个当前在内存中的页面作为 “牺牲者”。
-
接着将所需的页面数据从磁盘读入到分配好的物理页框中。
-
再更新当前进程的页表,修改映射关系。然后重新启动引起中断的指令。
-
页面设置过小:
- 页内碎片小: 最后一个页面浪费的空间少
- 磁盘 I/O 效率低,TLB 命中率下降,页表过大
-
页面设置过大:
- 页内碎片增加,加载无关数据
- 页表极小: 节省页表占用的内存。
- TLB 覆盖范围大: 极大地提高了 TLB 的命中率,提升 CPU 访存性能
- I/O 效率高: 一次磁盘操作能吞吐大量数据。
页表
- 在分页系统中,程序的地址空间(虚拟地址空间)被划分为固定大小的页,物理内存被划分为同样大小的页框。一个程序的虚拟页可以分散地存放在任何可用的物理页框中。
- 页表就是记录这种“分散存放”关系的“地图”或“地址簿”。
- 页表本质上是一个存储在内存中的数组。这个数组的索引是虚拟页号,数组的内容是页表项。
- 地址转换过程:
- MMU将虚拟地址拆分为
虚拟页号 (VPN)和页内偏移量 (Offset)。 - 以
VPN作为索引,在页表数组中找到对应的页表项(PTE)。 - 从页表项中取出
物理页框号 (PFN)。 - 将
PFN和Offset拼接,得到物理地址。
- MMU将虚拟地址拆分为
页表项
- 页表项的结构有:页框号,存在位,保护位,修改位,访问位,高速缓存禁止位。

- 存在位:
- 表示该页是否在物理内存中。1表示该页在物理内存中;0表示不在。访问一个P=0的页会触发缺页异常。
- 保护位:
- 控制对页面的访问权限,保障系统和进程的安全。用于指定该页面是否可读、可写、可执行。
- 修改位:
- 记录页面自被加载到内存后是否被写入过。这个位也常被称为脏位。
- 当页面首次被载入内存时,CPU会将此位设置为 0。
- 当有任何指令(如
MOV,STORE)写入该页面时,CPU的MMU硬件会自动将此位置 1。记录该页面被修改过,便于后面置换使用。 - 当操作系统需要将此页面换出内存以腾出空间时,它会检查修改位:
- 如果为 1(脏页),说明页面的内存副本比磁盘副本新,操作系统必须将该页面写回磁盘。
- 如果为 0(干净页),说明磁盘上的副本仍然是最新的,操作系统可以直接丢弃内存中的页面,无需执行耗时的磁盘写操作。
- 访问位:
- 记录页面近期是否被访问过(读或写)。
- 主要用途:为页面置换算法(如LRU的近似算法)提供决策依据。
- 高速缓存禁止位:
- 控制该页面对应的物理内存是否可以被CPU高速缓存。

- 逻辑地址 到 物理地址 的转换过程:
- 从逻辑地址计算页号p和页内偏移量w。
- 根据页表寄存器的页表长度m判断是否越界.
p < m不越界。 - 若不越界,通过页表起始地址f和页表项长度l,页号,找到页表中对应的内存块号。(地址为:
f+p*l) - 通过内存块号b,页面大小L,页内偏移量w计算出物理地址
b*L+w。
- 访问内存两次:
- 第一次访问内存中的页表得到物理块号。
- 第二次访问对应的物理地址处。
快表
-
由于计算机程序有一个现象:大多数程序总是对少量的页面进行多次的访问,而不是相反。因此,只有很少的页表项会被反复读取,而其他的页表项很少被访问。(局部性原理)
-
所以为计算机设置一个小型硬件设备,将虚拟地址直接映射到物理地址,而不必访问页表。这个设备为转换检测缓冲区 (TLB),也称快表。(位于高速缓存,而不是内存)
-
目的:缓存最近使用过的虚拟页到物理页框的映射关系。
-
它通常存在于MMU中,包含少量的表项。存储页表项的副本。
-
一个表项包含:
- 标签:存储的是虚拟页号 的一部分。
- 数据:存储的是对应的物理页框号,以及从原始页表项中复制过来的保护位、有效位、脏位等控制信息。

-
工作流程:
- 将一个虚拟地址进入MMU进行转换时,计算出页号和页内偏移量,查找TLB中所有表项进行匹配,判断虚拟页面是否存在其中。
- 如果匹配有效而且不违反保护位,则直接从TLB取出页框号,不必访问页表。
- 如果无有效匹配,就进行页表查询。然后在TLB中淘汰一个表项,用找到的新页表项代替。

-
如果快表命中:只需要一次访问内存,直接得到物理地址,直接访问物理地址处
-
如果未命中:需要访问两次内存,(页表+物理地址)
-
平均查找时间:命中率为\(p\),访问一次内存需要\(t_0\),访问快表需要\(t_1\)
页面置换算法
- 定义:缺页时如果没有空闲块,则要选择一个合适的页面置换。
- 已修改过的页面在被置换前应先保存到外存,而没有修改过的页面只是简单地覆盖。
先进先出页面置换算法FIFO
- 基本思想:优先淘汰最早进入的页面。
- 维护一个当前内存所有页面的链表,最新进入的页面放在表尾,最早进入的放在表头。当缺页中断时,淘汰表头的页面,将新页面放在表尾。
- Belady异常:为进程分配的物理块数增大时,缺页次数不减反增。
- 只有FIFO算法会产生Belady异常。
第二次机会页面置换算法
- 按先进先出页面置换的思路维护一个链表(最新进入的页面放在表尾,最早进入的放在表头)
- 每次缺页中断时,检查最老页面的R位,如果是0,直接淘汰;如果是1,则将R位清零,并放到链表的尾端。
- 第二次机会算法就是寻找一个在最近的时钟间隔内没有被访问过的页面。如果所有的页面都被访问过了,该算法就简化为纯粹的FIFO算法。

最优页面置换算法OPT(Optimal)
- 算法思想:将每个页面在该页面下次被访问前所要执行的指令次数作为标记,每次置换标记最大(标记大,说明要先执行更多的指令之后访问此页面)的页面。
- 优先淘汰未来最长时间内不会再被访问的那一页。
- opt需要提前知道未来的访问序列,在实际系统中不可能实现。
最近最久未使用页面置换算法LRU(Least Recently Used)
- 基本思想:优先淘汰最长时间没有被使用的页面。
- LRU在理论上可以实现性能好,最接近opt算法,但代价很高。
- 维护一个所有页面的链表,表头为最近最多使用的页面,表尾为最近最少使用的页面。每次访问内存时要更新整个链表。(找到该节点移动到表头的开销很大)
最近未使用页面置换算法NRU(Not Recently Used)
- 每个页面都有访问位(Reference bit) 和修改位(Modified bit)
- 一个页面被访问时,其访问位被置为1;一个页面被修改后其修改位被置为1。
- 页面的访问位定期地(如每隔20ms)被清为0,所以如果一个页面的访问位为0,则表示该页面在最近一个时钟周期没有被访问过。
- 所以一个页面有四种情况(RM位):
- 没有被访问,没有被修改
- 没有被访问,已经被修改
- 已被访问,没有被修改
- 已被访问,已被修改
- NRU(Not Recently Used)算法随机地从编号最小的一类中选一个页面淘汰。
时钟页面置换算法Clock
- 为每个页面设置一个访问位。
- 把所有页面放入类似于时钟的一个环形链表中。表针指向最老的页面。
- 发生缺页中断时,每次检查表针的页面,如果R位是0则淘汰,将新的页面插入这个位置,将表针向前移动一个位置;否则,清除该页面R位,将表针向前移动一个位置,直到找到R位是0的页面。

软件模拟LRU
最不常用页面置换算法NFU(Not Frequently Used)
- 每个页面有一个关联的软件计数器
- 操作系统定期扫描每个页面,将页面的R位的值累加于对应的计数器,所以计数器值最大的页面表示访问次数最多的页面。
- 优先置换计数器值最小的页面
缺陷
- 历史访问无法被“遗忘”,早期频繁的访问对现在仍有较大影响。NFU 对“近期访问”不敏感
老化算法(Aging)
-
用一个带“衰减”的计数器来逼近 LRU(Least Recently Used)算法。
-
每个页面维护一个 8 位(固定位数)的计数器。
-
每个时钟中断(例如每 20ms)执行以下操作:
- 将每个页面的计数器右移一位,这代表旧的访问“逐渐变得没那么重要”
- 若页面在本周期被访问(R=1),则将计数器的最高位置为 1
- 清除R位

-
counter 的位结构相当于“访问历史的滑动窗口”,counter 记录了过去 N 个时间片的访问历史,且越老访问所占的权重越低,N个时间片之前的访问历史被遗忘。
工作集页面置换算法(Working Set)
工作集与颠簸
- 一个进程当前正在使用的页面的集合称为它的工作集。
- 根据局部性原理可知:如果整个工作集都被装入到了内存中,那么进程在运行到下一运行阶段(例如,编译器的下一遍扫描)之前,不会产生很多缺页中断。
- 若内存太小而无法容纳下整个工作集,那么进程的运行过程中会产生大量的缺页中断,导致运行速度也会变得很缓慢。
- 若每执行几条指令程序就发生一次缺页中断,那么就称这个程序发生了颠簸。
- 刚刚换出的页面马上又要换入内存,刚刚换入的页面马上又要换出外存,这种频繁的页面调度行为称为抖动,或颠簸。
- 产生抖动的主要原因是进程频繁访问的页面数目高于可用的物理块数(分配给进程的物理块不够)
- 驻留集大小一般不能小于工作集大小
工作集模型
- 所以不少分页系统都会设法跟踪进程的工作集,以确保在让进程运行以前,它的工作集就已在内存中了。该方法称为工作集模型,其目的在于大大减少缺页中断率。在进程运行前预先装入其工作集页面也称为预先调页。请注意工作集是随着时间变化的。
- 在任一时刻\(t\),都存在一个集合,它包含所有最近k次内存访问所访问过的页面。这个集合\(w(k,t)\)就是工作集。
- 因为最近\(k=1\)次访问肯定会访问最近\(k>1\)次访问所访问过的页面,所以\(w(k,t)\)是\(k\)的单调非递减函数。
- 随着\(k\)的变大,\(w(k,t)\)是不会无限变大的,因为程序不可能访问比它的地址空间所能容纳的页面数目上限还多的页面,并且几乎没有程序会使用每个页面。

工作集页面置换
- 目标是动态地确定每个进程的工作集,并确保只有属于工作集的页面才保留在内存中。找出不属于工作集的页面并淘汰。
- 需要为每个页表项增加两个字段:
- 上次使用时间:记录该页面最后一次被访问(读或写)的时间戳。
- 访问位:记录页面近期是否被访问过
- 在处理每个表项时,都需要检查R位。
- 如果它是1,就把当前实际时间写进页表项的“上次使用时间”域,以表示缺页中断发生时该页面正在被使用。既然该页面在当前时钟滴答中已经被访问过,那么很明显它应该出现在工作集中,并且不应该被删除。
- 如果它是0,那么表示在当前时钟滴答中,该页面还没有被访问过,则它就可以作为候选者被置换。为了知道它是否应该被置换,需要计算它的生存时间(即当前实际运行时间减去上次使用时间),然后与做比较。如果它的生存时间大于 \(\tau\) ,那么这个页面就不再在工作集中,而用新的页面置换它。 扫描会继续进行以更新剩余的表项。
- 然而,如果R是0同时生存时间小于或等于τ,则该页面仍然在工作集中。这样就要把该页面临时保留下来,但是要记录生存时间最长(“上次使用时间”的最小值)的页面。
- 如果扫描完整个页表却没有找到适合被淘汰的页面,也就意味着所有的页面都在工作集中。在这种情况下,如果找到了一个或者多个R=0的页面,就淘汰生存时间最长的页面。在最坏情况下,在当前时间滴答中,所有的页面都被访问过了(也就是都有R=1),因此就随机选择一个页面淘汰,如果有的话最好选一个干净页面。

工作集时钟页面置换算法(WSClock)
- WSClock 使用 一个环形链表(时钟结构)维护页面,每个页面记录信息:
R位(访问位),Dirty位(修改位),lut(上次使用时间 \(\tau\)) - 全局变量,工作集窗口 \(\Delta\),如 200ms、1s,用来评估页面是否属于工作集
- 如果一个页面长时间没有被访问(\(CurrentTime - τ > Δ\)),说明它已经不属于工作集,应该被淘汰。
- 时钟指针指向页面P,
- R=1,清除R,指针前进
- R=0,检查页面是否在工作集中,若在工作集指针前进,否则检查修改位
- 页面未修改,淘汰
- 页面已修改,将页面异步写回磁盘,指针前进
- 如果一圈都没找到可淘汰页面,可以选择一个已经写回的脏页,或第一个R=0的脏页
多级页表
- 当虚拟地址空间十分巨大的时候,可以使用多级页表处理大内存的页表。
- 页表需要连续存放,当页表很大时,需要占用很多个连续的页框。
- 各级页表的大小不能超过一个页面。
二级页表

- 将32位的虚拟地址分为3部分:PT1(10)+PT2(10)+OFFSET(12)
- 二级页表分为顶级页表和二级页表,其中二级页表的每一项都对应\(2^{12}=4KB\)的地址块,顶级页表的每一项都对应一个二级页表的索引,对应\(2^{22}=4MB\)的地址空间。
- 该二级页表一共管理了\(2^{20}\)个页面。
- 顶级页表共有\(1024\)个表项,对应于虚拟地址的PT1部分。
- 二级页表同理,对应于虚拟地址的PT2部分。
- 将虚拟地址转化为物理地址的流程:取虚拟地址的PT1部分,以此为索引查询顶级页表,得到对应的二级页表号。取虚拟地址的PT2部分,以此为索 引查询二级页表,得到对应的物理地址块(对应页框号),加上偏移量得到物理地址。
- 两级页表访问内存(无快表)次数为3
- 第一次访问顶级页表,得到二级页表地址
- 第二次访问二级页表,得到物理块号
- 第三次计算出物理地址,访问物理地址处内存单元。
- 无快表时,n级页表访问一个逻辑地址,需要访问n+1次内存。
分段机制

- 基本思想:程序的地址空间按程序自然逻辑结构分段,每个分段作为一个独立分配单位,可以离散地分配到内存中。
- 核心思想:一个程序的内存空间应该由多个逻辑上独立的单元组成,每个单元对应一个逻辑段。
- 一个程序可以由几个段组成:
- 代码段:存放程序的指令。
- 数据段:存放全局变量、静态变量。
- 堆栈段:存放函数调用栈、局部变量。
- 可能还有堆段、命令行参数段等。
- 分段式内存管理就是将程序的这些逻辑段作为内存分配和管理的单位。每个段都有自己的名字、长度和属性。
工作原理


-
在分段系统中,一个虚拟地址不再是单一的一个地址,而是由两部分组成:
- 段号:指定了是哪个段(如代码段、数据段)。
- 段内偏移量:指定了在该段内部的哪个位置。
-
核心概念:
- 段寄存器:如
CS,DS,SS,ES,FS,GS。暂存当前正在使用的段选择子。 - 段选择子:是索引号(段号)+表指示位 (TI) + 特权级 (RPL)。
- 段描述符:描述了一个内存段的所有特征,包含段基址,段界限,属性权限等。
- 段描述符表(段表):存放在内存中的一个数组,数组的元素就是“段描述符”。分为全局描述符表 (GDT) 和局部描述符表 (LDT)。
- 段寄存器:如
-
段号的位数决定了每个进程最多可以分几个段。
-
段内地址的位数决定了每个段的最大0长度。
-
操作系统为每个进程维护一个段表。段表建立了逻辑段与物理内存区域的映射关系。
-
段表项(段描述符)的结构
- 段基址:该段在物理内存中的起始地址。
- 段长:该段的实际长度。
- 保护位:该段的访问权限(如:可读、可写、可执行)。
- 存在位:表示该段是否已加载到内存中。
- 修改位、访问位等。
-
工作流程:
- 当CPU执行一条指令,需要访问虚拟地址
(s, d)时,从段寄存器储存的段选择子获取段号 - MMU 使用段号 s 作为索引,查找进程的段表,找到第 s 个段表项。
- 检查合法性:偏移量检查,检查是否越界访问;权限检查,检查操作(读/写/执行)是否与段表项中的保护位匹配
- 如果检查通过,MMU 将段基址 b(段的起始物理地址)与段内偏移量 d 相加,得到最终的物理地址
(b,d)。
- 当CPU执行一条指令,需要访问虚拟地址
-
访问一个逻辑地址需要访问两次内存
- 第一次访问内存:查询段表,得到段基址和段长。
- 第二次访问物理地址处
-
优点:
- 分段系统没有内零头,但会出现外零头.
- 分段是根据程序的自然逻辑结构形成的,有利于程序和数据的共享。
分段与分页的区别与联系
- 分段系统和分页系统都是离散式的存储管理方式,都能较好地支持虚拟存储技术。
- 划分不同:
- 分页是对目标代码的逻辑地址按页面大小机械地划分形成,由系统进行划分,对用户不可见
- 分段则是根据程序的自然逻辑结构而形成的,对用户是可见的,更利于程序和数据的共享。
- 零头/碎片不同:
- 分段系统没有内零头,但会出现外零头;
- 分页系统没有外零头,但一个进程的最后一页所在物理块可能会出现内零头。
- 因为分段一般比较大,所以分段系统的内存利用率没有分页系统高。
段页式存储管理
- 基本思想:先分段,再分页。按进程的自然逻辑结构分段,段内分页。

工作原理
地址结构
- 在段页式系统中,一个虚拟地址(或逻辑地址)由三部分组成:
- 段号:指定是哪个逻辑段。
- 页号:指定在该段中的哪一页。
- 页内偏移量:指定在该页内的具体位置。
- 段号的位数决定了每个进程最多分几个段。
- 页号的位数,决定了每个段最多分多少页。
- 页内偏移量的位数,决定了页面的大小
段表页表
- 段表:每个进程一个段表。它的作用不再是直接指向物理内存的基址,而是指向该段的页表。
- 段表项包含:该段页表的起始物理地址、页表长度、保护位等。
- 页表:每个段一个页表。它的作用和纯分页系统中的页表一模一样,将虚拟页号映射到物理页框号。
- 页表项包含:物理页框号、存在位、修改位、保护位等。
地址转换过程
- MMU接受逻辑地址,使用虚拟地址中的 段号 s,找到进程段表中的第 s 个段表项。
- 段级检查:进行段级的保护和越界检查(例如,检查页号p是否超出了段的长度范围)。
- 找到页表:从段表项中取出该段的页表起始物理地址。
- 查找页表:MMU使用虚拟地址中的 页号 p 作为索引,在刚刚找到的页表中,定位到第 p 个页表项。
- 页级检查:检查页表项中的存在位和保护位。
- 合成物理地址:从页表项中取出物理页框号 f,与虚拟地址中的 页内偏移量 d 拼接,形成最终的物理地址。
- 需要访问3次内存
- 第一次:访问段表,得到页表起始地址
- 第二次:访问页表,得到物理块号
- 第三次:计算物理地址(物理块号+页内偏移量),访问内存中的对应的存储单元

优点
- 可以充分利用小的空闲区,避免分段系统中拼接的开销;又支持进程的自然逻辑结构,便于程序与数据的共享。逻辑清晰,易于共享和保护(分段的优点)。
- 没有外部碎片(分页的优点)
缺点
- 地址转换开销大:理论上,一次内存访问需要三次访存:访问段表,访问页表,访问目标指令或数据。
页面分配置换策略
驻留集
- 驻留集:指请求分页存储管理中给进程分配的物理块的集合。在采用了虚拟存储技术的系统中,驻留集大小一般小于进程的总大小。
- 若驻留集太小,会导致缺页频繁,系统要花大量的时间来处理缺页,实际用于进程推进的时间很少;
- 驻留集太大,又会导致多道程序并发度下降,资源利用率降低。所以应该选择一个合适的驻留集大小。
- 驻留集大小一般不能小于工作集大小
分配策略
- 固定分配:操作系统为每个进程分配一组固定数目的物理块,在进程运行期间不再改变。即,驻留集大小不变
- 可变分配:先为每个进程分配一定数目的物理块,在进程运行期间,可根据情况做适当的增加或减少。即,驻留集大小可变
置换策略
- 局部置换:发生缺页时只能选进程自己的物理块进行置换。
- 全局置换:可以将操作系统保留的空闲物理块分配给缺页进程,也可以将别的进程持有的物理块置换到外存,再分配给缺页进程。
固定分配局部置换
- 系统为每个进程分配一定数量的物理块,在整个运行期间都不改变。若进程在运行中发生缺页,则只能从该进程在内存中的页面中选出一页换出,然后再调入需要的页面。
- 这种策略的缺点是:很难在刚开始就确定应为每个进程分配多少个物理块才算合理。(采用这种策略的系统可以根据进程大小、优先级、或是根据程序员给出的参数来确定为一个进程分配的内存块数)
可变分配全局置换
- 刚开始会为每个进程分配一定数量的物理块。操作系统会保持一个空闲物理块队列。当某进程发生缺页时,从空闲物理块中取出一块分配给该进程;
- 若己无空闲物理块,则可选择一个未锁定的页面换出外存,再将该物理块分配给缺页的进程。
- 采用这种策略时,只要某进程发生缺页,都将获得新的物理块,仅当空闲物理块用完时,系统才选择一个未锁定的页面调出。被选择调出的页可能是系统中任何一个进程中的页,因此这个被选中的进程拥有的物理块会减少,缺页率会增加。
可变分配局部置换
- 刚开始会为每个进程分配一定数量的物理块。当某进程发生缺页时,只允许从该进程自己的物理块中选出一个进行换出外存。
- 如果进程在运行中频繁地缺页,系统会为该进程多分配几个物理块,直至该进程缺页率趋势适当程度;反之,如果进程在运行中缺页率特别低,则可适当减少分配给该进程的物理块。
预调页策略
- 根据局部性原理,一次调入若干个相邻的页面可能比一次调入一个页面更高效。但如果提前调入的页面中大多数都没被访问过,则又是低效的。
- 因此可以预测不久之后可能访问到的页面,将它们预先调入内存,但目前预测成功率只有50%左右。故这种策略主要用于进程的首次调入,由程序员指出应该先调入哪些部分。
请求调页策略
- 进程在运行期间发现缺页时才将所缺页面调入内存。 由这种策略调入的页面一定会被访问到,但由于每次只能调入一页,而每次调页都要磁盘I/O操作,因此I/O开销较大。

浙公网安备 33010602011771号