内存
内存
内存是用于存放数据的硬件,程序执行前需要先放到内存中才能被CPU处理
地址:
-
相对地址:逻辑地址
-
绝对地址:物理地址
程序运行前:
-
编译:源代码生成目标模块
-
链接:目标模块生成装入模块
-
静态链接:运行前将各目标模块及它们所需的库函数连接成一个完整的可执行文件(装入模块)
-
装入时动态链接:边装入边链接
-
运行时动态链接 :程序执行中需要该模块时,才对其链接;便于修改、更新、实现对目标模块的共享
-
-
装入:将装入模块装入内存
-
绝对装入:只适用于单道程序处理环境;编译、链接后得到的装入模块的指令直接就使用了绝对地址
-
静态重定位/可重定位装入:指令使用的是逻辑地址;由装入程序实现重定位将逻辑地址变为物理地址
-
一个作业装入内存时,必须分配其要求的全部内存空间,没有足够内存就不能装入
-
作业一旦进入后,运行期间就不能再移动,也不能再申请内存空间
-
-
动态重定位/动态运行时装入:装入程序在程序真正执行时(需要重定位寄存器),才把逻辑地址转化为物理地址
-
允许程序在内存中移动(改变重定位寄存器的值)
-
可将程序分配到不连续的存储区
-
程序运行前只需装入部分代码即可运行
-
运行期间可以根据动态申请分配内存
-
便于程序段的共享
-
可以向用户提供一个比存储空间大得多的地址空间
-
内存管理
-
内部碎片:分配给某进程的内存区域中,有些部分没有用上
-
外部碎片:内存中某些空闲分区由于碎片太小而难以利用
可以通过紧凑/拼凑技术解决
内存空间的分配与回收
连续分配管理方式
为用户分配的进程空间必须是连续的
-
单一连续分配:
内存:
-
系统区(低地址部分):存放操作系统相关数据
-
用户区:内存中只能有一道用户程序,独占整个用户区空间
优:实现简单,无外部碎片 ;可以采用覆盖技术扩充内存,不一定需要采取内存保护
缺:只能用户单用户、单任务操作系统;有内部碎片 ,存储器利用率极低
-
-
固定分区分配:将整个用户空间划分为若干个固定大小的分区(预先划分内存分区),每个分区中只装入一道作业
-
分区大小相等:缺乏灵活性,适用于一台计算机控制多个相同对象的场合
-
分区大小不等:增加了灵活性,可以满足不同大小进程需求,根据常在系统中运行的作业大小情况划分
操作系统需要建立分区说明表 ,来实现各个分区的分配与回收
-
-
优:实现简单,无外部碎片
-
缺:
- 当用户程序太大,可能所有分区都不能满足需求,不得不采用覆盖技术来解决,使得性能降低
- 有内部碎片,内存利用率低
-
动态分区分配/可变分区分配:在进程装入内存时根据进程大小动态建立分区 ,因此系统分区的大小和数目是可变的
-
优:没有内部碎片
-
缺: 有外部碎片
动态分区分配算法:
首次适应算法效果更好
-
首次适应(First Fit):按照地址递增顺序排列分区,每次从低地址开始查找,找到第一个能满足大小的空闲分区
-
最佳适应(BestFit):按照空闲分区容量递增顺序排列,尽可能多留下大片的空闲区,即优先使用更小的空闲区
- 缺:会留下越来越多很小的、难以利用的内存块,因此会产生很多外部碎片
-
最坏适应(Worst Fit):按照空闲分区容量递减顺序排列,每次分配优先使用最大的连续空闲区 ,分配后剩余的空闲区就不会太小,更方便使用
缺:若之后有大进程到达,就没有内存分区可用
-
邻近适应(Next Fit):按照地址递增顺序排列分区(循环链表),每次都从上次查找结束的位置开始检索
-
非连续分配管理方式
为用户分配的进程空间是分散的
-
基本分页存储管理:(地址是一维的)
把内存分为一个个大小相等的小分区,再按照分区大小把进程拆成一个个小部分
内存空间划分为大小相等分区,每个分区就是一个页框/页帧/内存块/物理块 ,每个页框有一个编号,页框号从0开始
将用户进程也分为与页框大小相等的一个个区域,称为页/页面 ,每个页有一个编号,页号从0开始
-
页框不能太大,否则会产生过大的内部碎片
-
每个页面与页框一一对应,各个页面存放顺序任意
逻辑地址结构:页号+页内偏移量
若页内偏移量有k位,说明一个系统中一个页面大小是2^k个内存单元
若页号有m位,说明一个进程最多有2^m个页面
例:32位二进制表示的逻辑地址,页面大小1KB
0号页逻辑地址空间:
00000000000000000000000000000000~ 00000000000000000000001111111111
1号页逻辑地址空间:
00000000000000000000010000000000~ 00000000000000000000011111111111
2号页逻辑地址空间:
00000000000000000000100000000000~ 00000000000000000000101111111111
地址转换(逻辑地址转化为物理地址):
-
计算出逻辑地址对应的页号 :逻辑地址/页面长度
(当页号≥页表长度(页表项的个数)时会发生越界)
-
页号对应页面在内存中的起始地址 :通过页表查询块号,块号*内存块大小即起始地址
每个页表项长度是相同的,页号是隐含的
例:假设某系统物理内存大小为4GB,页面大小为4KB,则每个页表项至少应该为多少字节(页表项长度)?
4GB = 2^32B,4KB = 2^12B
因此4GB的内存总共会被分为2^20 个内存块,因此内存块号的范围应该是0~2^20 -1因此至少要20个二进制位才能表示这么多的内存块号,因此至少要3个字节才够 -
逻辑地址在页面内的偏移量 :逻辑地址%页面长度
-
物理地址=页面始址+业内偏移量
-
-
两次访问内存:
-
查页表
-
访问目标内存单元
-
-
局部性原理:
-
时间局部性:若执行了程序中的某条指令/数据,不久后这条指令/数据很可能再次被执行
-
空间局部性:一旦访问了某个存储单元,不久后附近的存储单元也可能被访问
-
-
快表/联想寄存器(TLB):访问速度比内存快得多的高速缓存寄存器,用来存放当前访问的若干页表项以加速地址变换
-
若快表中没有目标页表项,则需要查询内存中的页表并放入快表
(快表中存放的是页表的一部分副本)
-
若命中则只需要访问一次内存 ;未命中则需要访问两次内存
-
-
慢表:内存中的页表
-
单级页表:
- 页表必须连续存放,当页表很大时,需要占用很多连续的页框
- 整个页表常驻内存
-
二级页表:(各级页表的大小不能超过页表长度)
逻辑地址:一级页号+二级页号+页内偏移量
从PCB中读出页目录表始址,再根据一级页号查页目录表,找到下一级页表在内存中的存放位置
访存次数:
-
访问内存中的页目录表
-
访问内存中的二级页表
-
访问目标内存单元
(假设没有快表机制)N级页表访问一个逻辑地址需要N+1次访存
例:某系统按字节编址,采用40位逻辑地址,页面大小为4KB,页表项大小为4B.
假设采用纯页式
存储,则要采用()级页表,页内偏移量为()位?页内偏移量12位,页号28位,页表长度10位
二级页表:18+10+12
三级页表:8+10+10+12
-
-
-
基本分段存储管理
按照程序自身逻辑划分为若干段,每个段都有一个段名,每段从0开始编制 ;每个段在内存中占据连续空间,但各段之间可以不相邻
-
地址空间是二维的
-
分段对用户是可见的
-
优:
- 用户编程更方便,程序可读性更高
- 比分页更容易实现信息的共享和保护
-
逻辑地址:段号+段内地址
-
段号的位数决定了可以分多少个段
-
段内地址位数决定了每个段的最大长度
-
各个段表项的长度是相同的;但各个段的长度是不同的
-
-
优 | 缺 | |
---|---|---|
分页管理 | 内存空间利用率高,不会产生外部碎片,只有少量页内碎片 | 不方便实现信息的共享和保护 |
分段管理 | 方便实现信息的共享和保护 | 会产生外部碎片 |
-
段页式存储管理
逻辑地址结构:段号+页号+页内偏移量(二维)
- 段号位数决定有多少个段
- 页号位数决定一个段最大有多少页
- 页内偏移量位数决定页面大小
一个进程只对应一个段表,可能对应多个页表
访存次数:
- 无快表:三次
- 快表命中:一次
内存空间的扩充(虚拟性)
-
覆盖技术:解决程序大小超过物理内存总和 (同一个进程或程序)
将程序分为多个段(模块),常用的段常驻内存,不常用的段在需要时调入内存
内存中有:
- 一个”固定区“ :常驻内存的段放在固定区中,调入后就不再调出
- 若干”覆盖区“ :不常用的段放在覆盖区中,需要用到时调入内存,不需要时调出内存
-
缺:对用户不透明,增加了编程负担:必须由程序员声明覆盖结构,操作系统完成自动覆盖
-
交换技术:内存空间紧张时,系统将内存中某些进程暂时换入外存,把外存中某些具备条件的进程换入内存(进程在内存与磁盘间动态调度)(不同进程或作业之间)
磁盘空间:
-
文件区:存放文件,追求存储空间的利用率,采用离散分配方式
-
对换区:只占磁盘空间小部分,被换出的进程数据存放在对换区,追求换入换出速度,采用连续分配方式(I/O速度比文件区快)
-
-
虚拟存储技术:
传统存储管理((非)连续分配):
-
作业必须一次性装入内存才可以开始运行
-
作业一旦装入内存必须驻留直至运行结束
高速缓冲技术:将近期会频繁访问的数据放到更高速的存储器中,暂时用不到的数据放到更低速存储器中
-
虚拟内存的最大容量由CPU寻址范围确定
-
虚拟内存的实际容量=min(内外存之和,CPU寻址范围)
例:某计算机地址结构为32位,按字节编址,内存大小为512MB,外存大小为2GB则
虚拟内存的最大容量为2^32B = 4GB
虚拟内存的实际容量= min (2^32B,512MB+2GB)= 2GB+512MB
虚拟内存:建立在离散分配内存管理方式之上
-
多次性:无需在作业运行时一次性装入内存,允许被分成多次调入内存
-
对换性:作业运行时无需常驻内存,允许换入换出
-
虚拟性:从逻辑上扩充了内存容量,使用户看到的内存容量远大于实际容量
虚拟内存的实现:缺页时未必发生置换
-
请求分页存储管理
基本分页存储+请求调页+页面置换(当某个页面被换出内存,快表中对应的表项也要删除)
缺页中断(内中断):当前执行的指令想访问的目标页面未调入内存
页面置换算法:
-
最佳置换算法:选择淘汰的页面是以后永不使用或最长时间内不再被访问,以保证最低的缺页率
操作系统无法提前预知页面访问序列,因此最佳置换算法是无法实现的
-
先进先出置换算法:每次选择淘汰的页面是最早进入内存的页面
-
Belady异常:当进程分配的物理块数增大,缺页次数不减反增
-
只有FIFO算法会产生Belady异常
-
算法性能差
-
-
最近最久未使用置换算法:每次淘汰的页面是最近最久未使用的页面
-
算法性能好;实现困难;开销大
-
-
时钟置换算法/最近未用算法:
-
每个页面设置一个访问位,再将内存中的页面都通过链接指针链接成一个循环队列
-
当某页被访问时,其访问位置为1
-
当需要淘汰一个页面时,只需检查页的访问位:
-
0:换出
-
1:置为0,暂不换出
-
-
继续检查下一个页面,若第一轮扫描中所有页面都是1,则将这些页面的访问位依次置为O后,再进行第二轮扫描(第二轮扫描中一定会有访问位为0的页面,最多会经过两轮扫描)
例:假设系统为某进程分配了五个内存块,并考虑到有以下页面号引用串:1,3,4,2,5,6,3,4,7
-
-
改进的时钟置换算法:只有被淘汰的页面被修改过,才需要写回外存
(访问位,修改位)
规则:
-
第一轮:从当前位置开始扫描到第一个(0,0)的帧用于替换;本轮扫描不修改任何标志位
-
第二轮:若第一轮扫描失败,则重新扫描,查找第一个(0,1)的帧用于替换;本轮将所有扫描过的帧访问位设为0
-
第三轮:若第二轮扫描失败,则重新扫描,查找第一个(0,0)的帧用于替换;本轮扫描不修改任何标志位
-
第四轮:若第三轮扫描失败,则重新扫描,查找第一个(0,1)的帧用于替换
最多会进行四轮扫描
-
-
-
请求分段存储管理
-
请求段页式存储管理
页面分配策略:
-
工作集:某段时间间隔内,进程实际访问页面的集合
(驻留集的大小不能小于工作集的大小)
-
驻留集:请求分页存储中给进程分配的物理块的集合
-
太小:缺页频繁,系统要花大量时间处理缺页,实际用于进程推进的时间少
-
太大:多道程序并发度下降,资源利用率降低
-
-
局部置换:缺页时只能选进程自己的物理块进行置换
-
全局置换:可以将操作系统保留的空闲物理块分配给缺页进程,也可以将别的进程持有的物理块置换到外存,再分配给缺页进程
局部置换 全局置换 固定分配 √ × 可变分配 √根据缺页频率动态增加或减少进程物理块数 √只要缺页就分配新物理块
调入:
-
预调页策略(运行前):根据局部性原理,一次调入若干相邻的页面(主要用于进程首次调入)
-
请求调页策略(运行时):运行期间发生缺页中断时才将缺页面调入内存
-
调入位置:
-
对换区空间足够:
-
缺少对换区空间:
-
-
抖动/颠簸(频繁页面调度)现象:刚换出的页面马上又要换入内存,刚换入的页面马上又要换出外存
(分配给进程的物理块不够)
-
地址转换
逻辑地址到物理地址
内存保护
各进程只能访问自己的内存空间
-
在CPU中设置一对上、下限寄存器,存放进程的上、下限地址
-
采用重定位寄存器(基址寄存器,存放起始物理地址)和界地址寄存器(限长寄存器,存放进程最大逻辑地址)