// 仅为学习记录,不做详细讲解
前言
本篇博文为当初考研时做的笔记,整个文章架构以当时考研时参考的书籍来完成的,现在回顾的时候发现写得并不“有机”,完全机械,不能读懂,但是对应试非常有用。如果你想对分页,分段,虚拟内存这些知识点有宏观且易懂的了解,推荐《程序员的修养–链接,装载与库》,《深入计算机系统》,《现代操作系统》或者《操作系统概念》,阅读相关章节即可。
简介
相比于连续分配管理方式,非连续允许一个程序分散装入不相邻的内存区域,这就好像把人体给肢解了一样。当然这不算完,分散的程序要构成有机体程序那么就要通过索引的方式再连接起来。
分类
以下是两种非连续管理方式的分类
基本分段存储管理方式
分段的总体思路是把程序需要的内存空间大小的虚拟空间映射到某个物理地址空间。如下图所示,对于程序 A,B 来说,都各自假设一段虚拟地址空间(A 是 0x00000000 ~ 0x00A00000,B 是 0x00000000 ~ 0x06400000),然后通过软件的映射函数来映射到物理空间(实际的映射由硬件完成)
分段的方式有两个好处
- 一个是程序地址隔离,A 程序越界访问 B 程序时,会有硬件出来阻止
- 另一个是解决了程序运行地址不确定的问题,这主要涉及的是程序换入换出的问题,不展开
分段的方式的缺点 - 内存使用效率低
分段
分段存储管理主要要从语义上来进行分段。比如一个程序由主程序,负责加法逻辑的程序
负责减法逻辑的程序,那么就可以按上述分成三段。每段从 0 开始分配一段连续空间(要
求段内连续,段间可以不连续),由此可见整个作业的地址空间是二维的
段的划分是在用户编程的时候完成的
NOTE:段号和段内偏移量必须显式提供
段表
段表的结构(段号,段长,本段在内存中的始址)
段表实现了从逻辑地址到物理内存区的映射
利用段表实现物理内存区的映射
地址变换机构
逻辑地址 A 到物理地址 E 之间的地址变换过程:
- 从 A 中提取段号 S 和 偏移量 W
- 比较段号 S 和段表长度 M,若 S>= M 则越界
- 找出段表项地址 = 段表始址 F + 段号 S*段表项长度
- 取出段表项中的段长 C ,比较段长 C 和 偏移量 W 的关系,若 W 大,则越界
- 计算物理地址 = 基址(段表始址)+W
NOTE:我们可以看到第 4 步中,和分页存储的计算发生了变化,,在分段中我们进行了两次比较,而分页中只需要一次
段的共享与保护
- 可重入代码(纯代码):不能修改的代码(可以共享的是可重入代码)
基本分页存储管理方式
分页存储的基本方法是把地址空间人为的分成固定大小的页,页的大小由硬件决定,一般处理器支持多种页的大小由操作系统进行选择,但同一时刻只能选择一种分页大小。
分页存储的概念
-
进程中的块,逻辑内存,虚拟内存:页,页面(要求页面大小为 2 的整数幂)
-
内存物理地址中的块:页框,帧
-
外存,磁盘(硬盘),备份存储中的块:块
【注】以上三者中的块,都是一样的大小,这也是分页存储的特点
【注】在《操作系统概念》这本书中,它将逻辑内存和虚拟内存视为同一概念,但我并不确定这里的虚拟内存是否是严格的虚拟内存,因为这里讲的是基本分页存储管理,而虚拟内存中的是请求分页存储管理,两种分页管理不完全相同,但很多书中,并未进行进一步的区分。 -
基本的分页存储管理逻辑图
在该图中,我们展示了一个基本的分页存储管理的逻辑图。CPU 将一个进程程序抽象成一个逻辑内存,然后将这个逻辑内存分成一个个的页,为了定位每一个页,就必须有一个页的偏移量(其实就是一页的长度)。这样我们就得到了逻辑地址的组成(具体可以看下面)。
我们知道,逻辑地址并不是真实存在于物理内存中,它只是将物理内存抽象成逻辑内存,好像一个进程独占了整个物理内存。
那么我要怎么从逻辑内存变成物理内存呢?这里就用到了页表,
- 地址结构
前一部分为页号,后一部分为页内偏移地址,当地址长度为 32 位时,地址最多允许 2^20 个页
- 页表
系统为每一个进程建立页表,用于作为内存地址和外存中的物理地址的映射,其组成为页号和块号。页表常驻内存。
特别的,页表项的第二部分与地址结构的第二部分共同组成物理地址
基本地址变换机构
我们知道页表用于记录程序的逻辑地址到内存的物理地址的映射,而地址变换机构就是利用页表的记录来实现这个转换过程的。
页表寄存器:正如前面所讲,页表常驻内存,所以当我们要使用页表时就必须找到它,它在内存中的地址由页表寄存器来实现。
特别的,只有在程序执行时才会读入页表的两个信息到页表寄存器
计算从逻辑地址到物理地址的映射
首先分析,整个映射过程其实就是让页表**动态化**,即我们只要找到页号,页号映射到
对应的页表项,页表项对应的块号,再由块号和地址结构的页内偏移量组合(回顾概念
5 )就可以得到结果
这个流程如下,假设页号为 P,页内偏移量 W,页面大小为 L,页表长度 M,逻辑地址为 A,物理地址为 E 的转换过程如下:
- 页号 P = A / L
- 页内偏移量 W = A % L
- 比较页号 P 和页表长度 M,如果 P>=M,则表示越界了
- 该页号对应的页表项 = 页表起始地址 + 页号 P *页表项长度
- 通过页表项找到对应的物理块号
- 物理地址 E = W + 物理块号 * L
特别的,页面大小,页框大小和块大小是一样的,以便一一对应
具有快表的地址变换机构
由上面的查找过程可知,我们存取一次数据(指令)至少需要两次访问内存,第一次是计算出数据(指令)对应的物理地址,第二次才是真正去取这个地址。这样速度上就慢了一半。
这就好像我周二要在某个规定的时间去 A 地拿某个东西,因为对 A 地不熟悉,为了保证按时到达,我周一就去 A 地一趟熟悉路线,但是周二才是真正去取这个东西。
于是我们设置了快表(顾名思义就是提高运行速度),用来存放当前访问的若干表项。
具有快表的分页机制的地址变换过程:
- 得到逻辑地址后先把页号和快表中的页号对比
- 如果找到匹配项,则通过快表找到对应的物理块号完成
- 如果没有,则要想之前的步骤一样去页表中取
两级页表
为了进一步提高内存的利用率,我们用两级页表的方式来压缩页表的空间。
关于两级页表在考试中只需要记住顶级页表最多只能由一个页面。
相关计算题的知识点补充
- 一页的大小 = 2 页内偏移量位数 , 偏移量通常是 12 位 一页的大小=2^{页内偏移量位数},偏移量通常是 12 位 一页的大小=2页内偏移量位数,偏移量通常是12位
- 页表最大占用的字节数 = 2 页号位数 ∗ 页表项大小 页表最大占用的字节数 = 2^{页号位数}*页表项大小 页表最大占用的字节数=2页号位数∗页表项大小
3. 段页式管理方式
由上图可见,只有一个段表。而段表映射每一段都有一个页表。一个进程中段表只有一个,而页表可以有多个。
其地址转换方式如下:段表 --> 页表始址 --> 页号 --> 形成物理地址
由此可见一次访问实际需要三次访问内存
参考与拓展
- 【学习笔记】内存的连续分配管理方式:这篇讲的是更加原始的内存管理方式
- 《程序员的自我修养 – 链接,装载与库》