💽 内存管理
首先需要区分几个概念:内存、磁盘、硬盘
内存:即运行内存,比如手机运行内存4G,断电后内存中数据全部丢失
磁盘:外部存储器,比如手机存储容量64G,断电后磁盘中数据不会丢失,但读取速度远远低于内存。外存中任何数据只有调入内存中才能真正使用
- 硬盘:磁盘的一种
- 软盘:磁盘的一种
硬盘也分 固态硬盘 和 机械硬盘:
在读写速度上面,固态硬盘肯定是完胜机械硬盘。
如果把系统装在固态盘和机械盘作比较的话,开机的速度根本不是一个级别的。而且,除了开机速度之外,在游戏的加载上也有很多不同点,相信很多人都遇到开局加载速度奇慢无比的人,这就是硬盘所决定的。
一、什么是内存,有何作用
内存可存放数据。程序执行前需要先放到内存中才能被CPU处理——缓和CPU与硬盘之间的速度矛盾
思考:在多道程序环境下,系统中会有多个程序并发执行,也就 是说会有多个程序的数据需要同时放到内存中。那么,如何区分各个程序的数据是放在什么地方的呢?
方案:给内存的存储单元编地址
二、什么是内存管理
操作系统作为系统资源的管理者,当然也需要对内存进行管理,要管些什么呢?主要包括以下四个方面:
- 内存空间的分配与回收
- 内存空间的扩充
- 地址转换
- 存储保护
1. 内存空间的分配与回收(传统存储管理)
传统的存储管理方式有以下两种:
a. 连续分配管理方式
连续分配:指为用户进程分配的必须是一个连续的内存空间。
单一连续分配
在单一连续分配方式中,内存被分为系统区和用户区。
系统区通常位于内存的低地址部分,用于存放操作系统 相关数据;
用户区用于存放用户进程相关数据。 内存中只能有一道用户程序,用户程序独占整个用户区 空间。
- 优点:实现简单;无外部碎片;可以采用覆盖技术扩充 内存;
- 缺点:只能用于单用户、单任务的操作系统中;有
内部碎片
(分配给某进程的内存区域 中,如果有些部分没有用上,就是“内部碎片”);存储器利用率极低。
固定分区分配
20世纪60年代出现了支持多道程序的系统,为了能在内存中装入多道程序,且这些程序之间又不会相互干扰, 于是将整个用户空间划分为若干个固定大小的分区,在 每个分区中只装入一道作业,这样就形成了最早的、最 简单的一种可运行多道程序的内存管理方式。
- 优点:实现简单,无外部碎片。
- 缺点:当用户程序太大时,可能所有的分区都不能满足需求,此时不得不采用覆盖技术来解决,但这又会降低性能;会产生内部碎片,内存利用率低。
动态分区分配
动态分区分配又称为可变分区分配
。这种分配方式不会预先划分内存分区,而是在进程装入内存时, 根据进程的大小动态地建立分区,并使分区的大小正好适合进程的需要。因此系统分区的大小和数目是可变的。
系统要用什么样的数据结构记录内存的使用情况?
b. 非连续分配管理方式
非连续分配:为用户进程分配的可以是一些分散的内存空间。
包括以下三种管理方式:(详细见下文 非连续分配的三种存储管理方式详解)
基本分页存储管理
把主存分为大小相等且固定的一页一页的形式,页较小,提高了内存利用率,减少了碎片。页式管理通过页表对应逻辑地址和物理地址。页与页之间不连续,但页的内部连续。
基本分段存储管理
页式管理虽然提高了内存利用率,但是页式管理其中的页实际并无任何实际意义。
段式管理把主存分为一段段的,每一段的空间又要比一页的空间小很多 。但是,最重要的是段是有实际意义的,每个段定义了一组逻辑信息,例如,有主程序段 MAIN、子程序段 X、数据段 D 及栈段 S 等。
段式管理通过段表对应逻辑地址和物理地址。段与段之间不连续,但段的内部连续。
段页式存储管理
段页式管理机制结合了段式管理和页式管理的优点。
简单来说段页式管理机制就是把主存先分成若干段,每个段又分成若干页.
也就是说 段页式管理机制 中段与段之间以及段的内部的都是不连续的。
2. 内存空间的扩充
在传统存储管理方式 的基础上引入了交换 技术、覆盖技术,使得内存利用率有所提 升,
并且能从逻辑上扩充内存容量。
对内存空间的扩充主要有三种方法:
a. 覆盖技术
将程序分为多个段(多个模块)。 常用的段常驻内存,不常用的段在需要时调入内存。
必须由程序员声明覆盖结构,操作系统完成自动覆盖。
缺点:对用户不透明,增加了用户编程负担。
覆盖技术只用于早期的操作系统中,现在已成为历史。
b. 交换技术
内存空间紧张时,系统将内存中某些进程暂时换出外存,把外存中某些已具备运行条件的进程换入内存(进
程在内存与磁盘间动态调度)
在上一章学过进程调度(低级调度),中级调度(内存调度)就是使用的交换技术,就是要决定将哪个
处于挂起状态的进程重新调入内存。
c. 虚拟存储技术(虚拟内存)
详细见下文
3. 地址转换/重定位
操作系统需要提供地址转换功能,负责程序的 逻辑地址 与 物理地址 的转换
为了使编程更方便,程序员写程序时应该只需要关注指令、数据的逻辑地址。而逻辑地址到物理地址的转换(这个过程称为地址重定位)应该由操作系统负责,这样就保证了程序员写程序时不需要关注物理内存的实际情况。
将逻辑地址空间重定位到物理地址空间的时机有三种:
- 程序编译链接时
- 程序装入内存时
- 程序执行时
① 静态重定位
静态重定位是在程序执行之前(编译链接时)进行重定位。对每个程序来说,静态重定位是在装入内存时一次性完成的,在程序运行期间不再进行重定位
优点:
- 无须硬件支持
缺点:
- 一是程序重定位之后就不能在内存中搬动了;
- 二是要求程序的存储空间是连续的,不能把程序放在若干个不连续的区域内。
② 动态重定位
动态重定位是在程序执行过程中进行地址重定位,更确切地说,是在CPU每次访问内存单元前才进行地址变换。动态重定位一般需要硬件支持。
优点:
- 目标模块装入内存时无需任何修改,因而装入之后再搬迁也不会影响其正确执行,这对于存储器紧缩、解决碎片问题是极其有利的;
- 一个程序由若干个相对独立的目标模块组成时,每个目标模块各装入一个存储区域,这些存储区域可以不是顺序相邻的,只要各个模块有自己对应的定位寄存器就行。
缺点:
- 需要硬件支持
4. 存储保护
操作系统需要提供内存保护功能。保证各进程在各自存储空间内运行,互不干扰
三、非连续分配—基本分页存储管理
1. 逻辑地址(相对地址) 物理地址(绝对地址)
比如在 C 语言中,指针里面存储的数值就可以理解成为内存里的一个地址,这个地址也就是我们说的逻辑地址,逻辑地址由操作系统决定。物理地址指的是真实物理内存中地址,更具体一点来说就是内存地址寄存器中的地址。物理地址是内存单元真正的地址。
2. 什么是分页存储?
将内存空间分为一个个大小相等的分区(比如:每个分区 4KB),每个分区就是一个“页框”(页框=页帧=内存块=物理 块=物理页面)。每个页框有一个编号,即“页框号”(页框号=页帧号=内存块号=物理块号=物理页号),页框号 从 0 开 始 。
将进程的逻辑地址空间也分为 与页框大小相等 的一个个部分, 每个部分称为一个“页”或“页面” 。每个页面也有一个编号, 即“页号”,页号也是从0开始。
操作系统以页框为单位为各个进程分配内存空间。进程的每个 页面分别放入一个页框中。也就是说,进程的页面与内存的页 框有一一对应的关系。 各个页面不必连续存放,可以放到不相邻的各个页框中。
3. 重要的数据结构——页表
为了能知道进程的每个页面在内存中存放的位置,操作系统要为每个进程建立一张页表。
注:页表通常存在PCB(进程控制块)中
4. 页表管理中重要的两个问题
- 问题1:逻辑地址到物理地址的转换要快。(快表解决)
- 问题2:解决逻辑地址空间大,页表也会很大的问题。(多级页表)
4. 快表TLB-解决问题1
为了解决虚拟地址到物理地址的转换速度,操作系统在 页表方案 基础之上引入了 快表TLB 来加速虚拟地址到物理地址的转换。
快表,又称联想寄存器(TLB,translation lookaside buffer ),是一种访问速度比内存快很多的 高速缓存(TLB不是内存!),用来存放最近访问的页表项的副本,可以加速地址变换的速度。 与此对应,内存中的页表常称为慢表。
若快表命中,就不需要再访问内存了,若快表中没有目标页表项,则需要查询内存中的页表
由于采用页表做地址转换,读写内存数据时 CPU 要访问两次主存。有了快表,有时只要访问一次高速缓冲存储器,一次主存,这样可加速查找并提高指令执行速度。
使用快表之后的地址转换流程是这样的:
- 根据逻辑地址中的页号查快表;
- 如果该页在快表中,直接从快表中读取相应的物理地址;
- 如果该页不在快表中,就访问内存中的页表,再从页表中得到物理地址,同时将页表中的该映射表项添加到快表中;
- 当快表填满后,又要登记新页时,就按照一定的淘汰策略淘汰掉快表中的一个页。
5. 两级页表-解决问题2
单级页表存在的问题:
- 页表必须连续存放,因此当页表很大时,需要占用很多个连续的页框。(多级页表解决)
- 没有必要让整个页表常驻内存,因为进程在一段时间内可能只需要访问某几个特定的页面。(可以在需要访问页面时才把页面调入内存(虚拟存储技术);也可以在页表项中增加一个标志位,用于表示该页面是否已经调入内存)
把页表再分页并离散存储,然后再建立一张页表记录页表各个部分 的存放位置,称为页目录表,或称外层页表,或称顶层页表。若分为两级页表后,页表依然很长,则可以采用更多级页表
四、非连续分配—基本分段存储管理
与“分页”最大的区别就 是——离散分配时所分配 地址空间的基本单位不同
1. 什么是分段
进程的地址空间:按照程序自身的逻辑关系划分为若干个段,每个段都有一个段名(在低级语言 中,程序员使用段名来编程),每段从0开始编址
内存分配规则:以段为单位进行分配,每个段在内存中占据连续空间,但各段之间可以不相邻。
分段系统的逻辑地址结构由段号(段名)和段内地址(段内偏移量)所组成。
段号的位数决定了每个进程最多可以分几个段
段内地址位数决定了每个段的最大长度
2. 段表
程序分多个段,各段离散地装入内存,为了保证程序能正常运行,就必须能从物理内存中找到各个逻辑段的存放位置。为此,需为每个进程建立一张段映射表,简称“段表”。
- 每个段对应一个段表项,其中记录了该段在内存中的起始位置(又称 “基址”)和段的长度。
- 各个段表项的长度是相同的
3. 分段、分页管理的对比
页是信息的物理单位。分页的主要目的是为了实现离散分配,提高内存利用率;实现虚拟内存,从而获得更大的地址空间。
分页仅仅是系统管理上的需要,完全是系统行为,对用户是不可见的。
段是信息的逻辑单位。分段的主要目的是更好地满足用户需求。
一个段通常包含着一组属于一个逻辑模块的信息。分段对用户是可见的,用户编程时需要显式地给出段名。
页的大小固定且由系统决定。
段的长度不固定,决定于用户编写的程序。
分页的用户进程地址空间是一维的,程序员只需给出一个记忆符即可表示一个地址。
分段的用户进程地址空间是二维的,程序员在标识一个地址时,既要给出段名,也要给出段内地址
分段比分页更容易实现信息的共享和保护。不能被修改的代码称为纯代码或可重入代码(不属于临 界资源),这样的代码是可以共享的。可修改的代码是不能共享的
访问一个逻辑地址需要几次访存?
分页(单级页表):第一次访存——查内存中的页表,第二次访存——访问目标内存单元。总共两次访存
分段:第一次访存——查内存中的段表,第二次访存——访问目标内存单元。总共两次访存
与分页系统类似,分段系统中也可以引入 快 表 机构,将近期访问过的段表项放到快表中,这样可以少一次访问,加快地址变换速度。
五、虚拟内存
1. 传统存储管理方式的特征、缺点
一次性:作业必须一次性全部装入内存后才能开始运行。这会造成两个问题:
①作业很大时,不能全部装入内存,导致大作业无法运行;
②当大量作业要求运行时,由于内存无法容纳所有作业,因此只有少量作业能运行,导致多道程序并发度下降。
驻留性:一旦作业被装入内存,就会一直驻留在内存中,直至作业运行结束。事实上,在一个时间段 内,只需要访问作业的一小部分数据即可正常运行,这就导致了内存中会驻留大量的、暂时用不到的 数据,浪费了宝贵的内存资源。
2. 局部性原理
- 时间局部性:如果执行了程序中的某条指令,那么不久后这条指令很有可能再次执行;如果某个数据被访问过,不久之后该数据很可能再次被访问。(因为程序中存在大量的循环)
- 空间局部性:一旦程序访问了某个存储单元,在不久之后,其附近的存储单元也很有可能被访问(因为很多数据在内存中都是连续存放的,并且程序的指令也是顺序地在内存中存放的)
3. 虚拟内存的定义
基于局部性原理,在程序装入时,可以将程序中很快会用到的部分装入内存,暂时用不到的部分留在外存, 就可以让程序开始执行。
在程序执行过程中,当所访问的信息不在内存时,由操作系统负责将所需信息从外存调入内存,然后继续 执行程序。
若内存空间不够,由操作系统负责将内存中暂时用不到的信息换出到外存。
在操作系统的管理下,在用户看来似乎有一个比实际内存大得多的内存,这就是虚拟内存。(操作系统虚拟性的一个体现,实际的物理内存大小没有变,只是在逻辑上进行了扩充。)
4. 虚拟内存的主要特征
虚拟内存有一下三个主要特征:
- 多次性:无需在作业运行时一次性全部装入内存,而是允许被分成多次调入内存。
- 对换性:在作业运行时无需一直常驻内存,而是允许在作业运行过程中,将作业换入、换出。
- 虚拟性:从逻辑上扩充了内存的容量,使用户看到的内存容量,远大于实际的容量。
5. 如何实现虚拟内存技术
虚拟内存技术,允许一个作业分多次调入内存。如果采用连续分配方式,会不方便实现。
因此, 虚拟内存的实现需要建立在离散分配(非连续分配)的内存管理方式基础上。
a. 请求分页存储管理
请求分页存储管理
与 基本分页存储管理
的主要区别:
- 在程序执行过程中,当所访问的信息不在内存时,由操作系统负责将所需信息从外存调入内存,然 后继续执行程序。(操作系统要提供 请求调页功能, 将缺失页面从外 存调入内存 )
- 若内存空间不够,由操作系统负责将内存中暂时用不到的信息换出到外存。 (操作系统要提供页面置换(详细见下文 六、请求分页存储-页面置换算法)的功能, 将暂时用不到的页面换出外存)
页表机制
缺页中断机构
在请求分页系统中,每当要访问的页面不在内存时,便产生一个缺页中断,然 后由操作系统的缺页中断处理程序处理中断。 此时缺页的进程阻塞,放入阻塞队列,调页完成后再将其唤醒,放回就绪队列。
如果内存中有空闲块,则为进程分配一个空闲块,将所缺页面装入该块,并修改页表中相应的页表项。
如果内存中没有空闲块,则由页面置换算法选择一个页面淘汰,若该页面在内存期间被修改过,则要将其写回外存。未修改过的页面不用写回外存。
缺页中断是因为当前执行的指令想要访问的目标页面未调入内存而产生的,因此属于内中断
一条指令在执行期间,可能产生多次缺页中断。(如:copy A to B,即将逻辑地址 A 中的数据复制到逻辑地址 B,而 A、B 属于不同的页面,则有可能产生两次中断)
地址变换机构
- 首先检查页面是否在内存中;
- 若页面不在内存中,请求调页(查到页表项时进行判断)
- 若内存空间不足,页面置换(需要调入页面,但没有空闲内存块时进行)
- 页面调入内存,需要修改请求页表中新增的表项
b. 请求分段存储管理
建立在分段存储管理之上,增加了请求调段功能、分段置换功能。
请求分段储存管理方式就如同请求分页储存管理方式一样,在作业开始运行之前,仅装入当前要执行的部分段即可运行;
在执行过程中,可使用请求调入中断动态装入要访问但又不在内存的程序段;
当内存空间已满,而又需要装入新的段时,根据置换功能适当调出某个段,以便腾出空间而装入新的段。
c. 请求段页式存储管理
六、请求分页存储—页面置换算法
1. 最佳置换算法 OPT
Optimal replacement algorithm
所选择的被换出的页面将是最长时间内不再被访问的,通常可以保证获得最低的缺页率。
是一种理论上的算法,因为无法知道一个页面多长时间不再被访问。
举例:一个系统为某进程分配了三个物理块,并有如下页面引用序列:
1 | 7,0,1,2,0,3,0,4,2,3,0,3,2,1,2,0,1,7,0,1 |
开始运行时,先将 7, 0, 1 三个页面装入内存。当进程要访问页面 2 时,产生缺页中断,会将页面 7 换出,因为页面 7 再次被访问的时间最长。
2. 先进先出置换算法 FIFO
选择换出的页面是最先进入的页面。
该算法会将那些经常被访问的页面也被换出,从而使缺页率升高。
3. 最近最久未使用置换算法 LRU
Least Recently Used
虽然无法知道将来要使用的页面情况,但是可以知道过去使用页面的情况。
LRU 将最近最久未使用的页面换出。
举例:一个系统为某进程分配了四个物理块,并有如下页面引用序列:
1 | 1, 8, 1, 7, 8, 2, 7, 2, 1, 8, 3, 8, 2, 1, 3, 1, 7, 1, 3, 7 |
4. 时钟置换算法 Clock / 最近未用算法 NRU
为每个页面设置一个访问位,当被访问时,访问位置为1。
当发生缺页中断时,NRU 算法只需检查页的访问位,如果是0(未被访问),就将该页换出;
如果是1,就将其置为0,暂不换出,继续检查下一个页面