各位同学大家好
今天我们开始介绍存储管理
计算机系统当中除了处理能力之外
还有存储能力 存储能力相当于
我们有一系列基本的存储介质
我们要在这些介质当中
来存我们的代码和数据
为此我们在这里具体的来说
对于计算机系统来讲
它的体系结构当中就约定了
我哪些地方可以来存数据
然后在存数据的这些地方
既包括CPU里的寄存器 也包括内存和外存
这几种不同的存储介质
它的容量 速度 价格都是不一样的
为了组织一个合理的系统
我们把计算机系统当中的存储
组织成了一个层次结构
针对这种层次结构下的存储单元
操作系统需要对它进行管理
操作系统当中的存储管理
实际上就是用来管理这些存储介质的
最基本的管理要求是说
我们一个进程需要使用存储单元的时候
需要从操作系统分一块给它
等它不用的时候还给操作系统
这是它最基本的分配和释放的管理要求
针对这种内存管理的要求
我们来看系统结构当中
有哪些因素对它有影响
这个图实际上是我们在前面
讲系统结构的时候就说过了
计算机系统包括CPU 内存和I/O设备
我们在上一次课里
也说到过CPU在加电的时候
我们关心各个寄存器的初始状态
那么在今天讲存储的时候
我们会更多关注与存储相关的内容
比如说在CPU里头
我们可以往寄存器存内容
寄存器可以存数据
但是寄存器它的容量是非常小的
通常是32位 64位的寄存器
能存的数据也就几十个字节
或者几百个字节这种尺度
然后我们说内存是更多的存数据的地方
我们在前面说到过它只是说能存就行了
在这我们需要更进一步去说
计算机系统当中内存
它的最小访问单位是字节 也就是8bit
而通常我们所说的计算机系统是32位的总线
那所谓32位总线也就相当于我一次读写
可以从内存当中读或者写32位
也就是4字节
这样以来我们读写的速度就会快了
如果针对这种特点
你在由于一次读写32位的有地址对齐的事
你在访问的时候就不能
从任意的地方开始一个四字节
有可能这个读写就会被分成两次
还有一个是说我们在CPU里头
你还会看到高速缓存
高速缓存是什么意思呢
在你进行读写指令
或者指令执行过程中
访问数据都需要从内存里读数据
这个时候如果说我有大量数据要读写
而且我会重复利用的话
我在CPU里加上高速缓存
那这样的话它的读写速度会更快
这个时候整个读写效率会提高
所以在CPU里加了高速缓存
这几个部分都对我们存储管理
有至关重要的影响
所以大家在实际做操作系统的
储存管理实现的时候
你必须很准确的了解对应的CPU的结构
那么对于我们操作系统课来讲
我们讲的是X86的系统
那这底下是英特尔的X86的手册
里头要想查最详细的内容
从这个手册里就可以查到
接下来我们更详细来看存储的层次结构
那么我们刚才已经说了
在CPU里头有两级缓存 这两级缓存
我们如果说你在读写数据或者指令的时候
在缓存里已经有相应的内容
事先已经读过
那这个时候我就直接可以从缓存里拿到
这个时候速度是最快的
然后说如果在这里头缓存不命中
那这个时候你就必须上内存里去读
而在上面这部分 我们在写程序的时候
你是感觉不到L1L2 hash的存在的 实际为啥呢
原因在于这部分完全是由硬件在做控制
你写的程序不能显示的使用到它们
而内存的访问就需要
使用到操作系统的控制
如果说你在内存里访问的时候仍然找不到
这个时候还有可能我是存到外存里头去
那么存到外存里头呢把它读进来
你再进行访问
这个时候就需要用到操作系统的控制
那在这个体系结构当中
我们可以看到从CPU内部
一直到硬盘的外部
这几个速度差的非常大的
具体差到什么程度 我们这里有一张表
最快的跟你CPU的主频是一样的
那就是几个纳秒我就能访问到
最慢是几个毫秒 这两者之间
毫秒 微妙 纳秒
差将近是百万倍的数量级
所以在这套体系当中
要想把它协调成一个有机的整体
实际上对于存储管理来讲
它的挑战性还是很大的
对于操作系统来说
我们存储管理最后想达到什么效果
我们在这给大家一个描述
首先我们看到系统当中的存储 内存
我们刚才说了是以字节为单位进行访问
每一个字节有自己的一个地址
这个地址是物理地址
然后说我如果数据存到外存里了
比如像磁盘 这是外存
磁盘的访问有扇区编号
每一个扇区是512个字节最小单位
那这是你能够读写存储的最基本的内容
而写程序的时候我们希望看到的情况是什么
是我有若干个进程 每一个进程
它们都有共同的一部分的地址空间
是操作系统的内核
然后每一个应用程序自己又是不一样的
它们各自有各自的内容
我希望在各自写这些内容的时候
它们的地址是可以重叠的
相互之间是不干扰的
这是我们希望见到的状态
把下面这种内存的状态
转变成我们上面逻辑的理想状态
我们在中间加了一层存储管理单元
存储管理单元就把逻辑地址空间
转变成物理地址空间 这个时候说
我实际操作系统代码存在哪呢 存在这头上
通常情况下是在内存里头的
而进程的地址空间
随着它们运行的转换
有些是在内存里头
有些是放在外存里头的
这个转换的过程
由中间的存储管理单元来完成
如果说我们能做到这样一步 实际就相当于
存储管理要达到效果是抽象
把线性的物理地址编号
转变成抽象的逻辑地址空间
然后我需要在这里头对地址空间进行保护
每一个进程只能访问自己的空间
尽管说在内存里它们是相邻存放的
与此同时我们还要方便共享
比如说在我们这里头大家可以看到的
操作系统的内核的代码
是各个进程都是一样的
或者说绝大部分是一样的 这种一致
如果说每个进程地址空间是相互保护的
不能访问 这段你就得存多份
这个效率是低的
我们希望能够很好地把保护和共享统一起来
这个目标是有一些矛盾的
与此同时我们还希望它实现更好的虚拟化
这说的是我们每个进程的
地址空间编号都是一样的
但是实际上每个进程
都有自己一段用户地址空间
到这里实际上物理地址空间
存的位置是不一样的
但是给每个进程看到的都是
一个区域一致的一个地址空间
甚至于说我们在逻辑地址空间里
看到的可以存数据的地方
它的大小是大于你的物理内存的总量的
我们看到实际上要想实现存储管理的
抽象 保护 共享和虚拟化
实际上这几个目标还是很有挑战的
具体说起来
我们在操作系统里
可能采用一些什么样办法
我们第一个办法就是重定位
实际上在最早的计算机系统当中
它是直接使用总线上的
物理地址来写你的程序
我要想读写某个内存单元 它在什么位置
我们在程序里见到的就是它的物理地址
但是这种做法实际上有很大局限
你写程序只能在指定类型的机器上运行
我们第一个可以让它做灵活
实际上相当于我可以整块的搬
就是我们这里说的重定位
如果说我们现在看到地址访问的时候
说每一个地址是用一个段地址
加一个偏移来表示
实际上就是从重定位这个地方来的
有了重定位之后我通过移
我只需要改我的段寄存器的地址
这个程序就能运行了
这是第一种做法 为了实现它
我在程序和操作系统里头
都需要有相应的支持
这些支持我后面会来说
接下来一个问题是说我们在这里头
在重定位的时候
我一个进程分的存储空间
是一个连续的空间
我不可以把两个交错起来
实际上这是一个很大的限制
我们希望它能够不连续
实际上我们在写程序的时候
它的逻辑结构
它并不是一个必须连成一片的区域
而是说我们把程序分成数据 代码 堆栈
这三个部分是相对独立的
它不会说我从堆栈里头
直接去访问代码段里的内容
也很少有这种情况
我从代码段里直接去访问数据段的内容
依据这种情况
我至少可以把代码 数据和堆栈分成三块
每一块我要的空间就会变少了
这就是我们这里的分段
分段仍然你是需要一段的内容
还是需要连续的
这个要求仍然足够高
接下来我们会说分页
分页实际上就是把内存分成最基本的单位
就好比说我们要盖一栋楼
盖一栋楼需要各种各样的建筑材料
但是我们用到的最基本的
就是一块一块的方砖
你可以把它加在一起之后
变成你需要的各种各样的形状
我们也希望是从最小的单位 一页
来构建你所需要存储区域
当然你说我们最小的单位
那就用一个字节不就挺好的吗
但是实际上这个时候你一个字节的话
你在访问的时候 它开销就力度太细
以至于管理的时候难度很高
所以我们在这要选一个合适的大小
这一块最基本的单位是一个连续区域
这就是我们这里的页 基于这个来构造
你所需要的存储空间的内容
在这个基础之上
我们希望把数据存到硬盘上
而硬盘外存上的数据和内存上的数据
它们俩之间的倒换是由操作系统内部来实现
这个时候就希望看到的是
程序是一个逻辑的地址空间
甚至这个逻辑地址空间
是大于物理内存的空间
那这就是我们的虚拟存储了
在我们实际上操作系统里
基本上就是这样几种方式来管理内存
当然我们说所有这些管理办法
它对硬件的依赖程度都是非常高的
比如说MMU
存储管理单元里的结构是什么样的
然后我们CPU能够识别的页表是什么样的
这些都直接影响到你存储管理方式的实现
到这我们对内存管理的基本情况有一个介绍