各位同学我们这次课主要讲的是
操作系统的第二个实验
关于物理内存的管理
那我们原理课里面
其实对这个内存管理做了很深入的讲解
主要是说物理内存它的相关的一些细节
比如说怎么去建立映射关系
物理内存相应的一些它的作用等等
给大家做了一个讲解
那我们这里面是想结合实验让大家对
这个物理内存有一个更深入的理解
看看操作系统怎么来
对物理内存进行有效的管理
这里面有两个比较困难的地方
第一个是关于特权级
因为我们说操作系统加上我们的CPU
给我们不同的应用程序提供一个隔离的空间
这点需要有一个保护机制 那这是一个
第二个我们的应用程序不能够随随便便的去
访问我们的操作系统的空间
这一点也是通过保护机制来实现的
这里面有一个特权级这么一个概念
我们希望通过lab2让大家能够知道
操作系统怎么利用CPU的硬件机制来建立
所谓的保护机制
这是第一个特权级这一块给大家做一个讲解
第二部分是关于内存管理单元
X86的内存管理单元
那我们这里面讲的更多的是基于X86的
MMU 这个内存管理单元
来看我们操作系统怎么利用
这个MMU的功能来实现内存的映射
这实际上就是说可以把
我们的虚拟的连续地址空间
映射到分散的离散的物理空间里面去
这两者之间是有差别的
那么这个差别是通过我们的X86内存管理单元
在操作系统的管理之下来实现的
那这两点需要大家能够理解
所以我们这一讲主要是给大家讲一下
硬件到底做了什么事情
我们操作系统怎么利用
这个相应的一些机制来完成这些功能
从而可以实现操作系统物理内存的管理
首先我们看一下X86的特权级
那首先我们要知道就是这个硬件
我们CPU到底提供了几种特权级
然后我们的程序在跑的时候
我们的CPU到底当前处于哪样特权级
就是说你应用程序到底处于什么特权级
你怎么知道的 你的应用程序是在内核态跑的
还是在用户态跑的
第三个我们说我们的应用程序跑在用户态
但可能过了一会儿又跑到内核态
我们操作系统在运行 那么这个来回的切换
涉及到一个所谓的特权级的切换
那这个切换过程怎么实现的
这是我们说这一节里面要讲的内容
就是X86特权级的一个问题
好 那我们接下来看一下 第一个是了解特权级
我们看一下X86特权级
X86特权级你可以看到它有0 1 2 3
四个特权级 其中内核是处于0特权级
因为就是我们特权级最高的地方就在Level 0
那么我们的应用程序一般是放在Level 3
就是Applications 这个应用程序放在Level 3
那么也意味着应用程序
无法去破坏内核里面的代码或者数据
但是它可以得到内核态代码的服务
它可以得到它的服务
同时看到还有Level 1 Level 2
那么当时intel设计CPU的时候考虑比较灵活
说我们还有其它一些应用
也许需要更多的层级来做区分
但其实在我们现在的
操作系统设计里面一般来说
只需要两级
就是一个操作系统Kernel有一个级别
一个是我们应用程序有一个级别
就够了 基本上就够了
所以说对于我们lab2这个实验来说
它只需用Level 0和Level 3就够了
一个是用来给我们操作系统ucore来跑的
来访问数据和运行
执行一些特权指令等等
那么另外一个是我们应用程序
它跑到Level 3 这是我们说X86特权级里面
我们的操作系统怎么去利用的
那对于我们常见的操作系统
比如说Linux Windows等等
基本上也都是用了0和3这两个级别就够了
这是我们说X86特权级的一个情况
那我们说有一些指令它只能在ring 0里面执行
而这些指令是X86的一些特权指令
那这些指令一般也是我们操作系统来完成的
那我们应用程序
如果它一开始运行在用户态的时候
如果它要去执行这些特权指令
比如我们说的修改页表 响应中断
访问内核区域的数据的时候
比如我们说访问数据段 访问页表
还有访问中断服务例程去执行的时候
那就会产生异常 我们前面讲到
有中断 有异常 这是说的产生异常
产生异常之后就会阻止你继续执行
这是一种典型的保护机制
那么它是靠我们操作系统会做一个设置
在我们CPU这个硬件的管理之下来确保
我们的一般的应用程序处在
我们刚才说的处于ring 3
属于一般的特权级的情况下
它无法去访问ring 0下面的一些
所可以使用的一些特权指令
和对应的那些数据等等
那前面我们看到CPU在很多情况下
会做特权级检查 具体它怎么来做的
我们需要进一步去了解一下细节
接下来我们会接下来给大家介绍一下
包括选择子以及后面的描述符
怎么结合在一起来完成
这一条指令执行的特权级检查
那么为什么要这么做的呢
我们其实很重要的一点就是
我们在完成lab1 lab2以及后续的lab的时候
经常会出现各种各样的错误
而这些错误有一大部分情况
是跟特权级相关的
你如果理解了为什么会产生
这些异常或者错误
那么你就可以知道到底哪些地方你写错了
所以说我们这边需要了解内部CPU
是怎么来完成这个特权级检查的
我们首先介绍个概念
第一个概念是什么呢 段选择子
段选择子是位于我们的段寄存器里面的
那么我们可以说一个段
有代码段 有数据段
也意味着你的程序在代码段里执行
你的数据位于数据段空间中
那么一条指令去执行 它就会去访问
我们的代码段和数据段
而这个RPL以及后续讲到的CPL
它其实就是一个位于数据段
一个位于代码段
合在一起再和我们段描述符里面
所对应的DPL进行比较
有点复杂 没关系
大家可以逐步来看一下是怎么一回事
首先我们介绍的段选择子
这个段选择子第二步我们可以看到
它的DPL是位于什么呢
段描述符里面的 段描述符是什么东西
我们前面已经讲到过
段描述符是用来表示一个段的特征的
一个很重要的数据信息
我们这里面关注的跟特级权相关的
包括了一个DPL
就是跟这个段所相关的一个特权级
我们也看到了
前面说我们一项指令要访问一个段
那这个代表了这一个段
这一个数据段或者代码段
它特级权是什么样的
也意味着它前面讲的RPL和
这里面的DPL做一个比较
看这个特权级是否符合
很简单一个想法
就是我要去访问某一个数据
首先我的特权级要足够高
我才能访问某一个数据
那你的特权级在什么地方呢
那么大家可以看到
其实我们在前面lab1中已经提到过有中断
我们讲过中断
那中断 异常 陷入
其实都还是需要通过一定的特权级检查
所以在里面中断门
中断门是我们说前面讲到的
中断描述符表里面的重要组成部分
中断门 陷入门里面 它都有对应的DPL
这也是用来表明这个中断和
对应的陷入大致特权级的展现
我们前面已经讲到了
有段描述符 有中断门 有陷入门
那么这几个它们都有对应的DPL
而我们在产生中断
或者完成一次内存访问的时候
都会有对应的CPL和RPL
那么这个CPL RPL和DPL之间
有一个对应的特权级检查 从而可以确保
当前这个操作是否会产生特权级的一个错误
或者能够正常的通过特权级的检查
能够继续完成相应的操作 这就是我们说
这个特权级检查里面需要考虑的问题
我们看看怎么来比较
我们知道RPL DPL和CPL
我们是讲三个概念 RPL是处于数据段
比如说我们的DS ES FS和GS
那么它这里面是存了一个RPL
是当前你要访问的数据段对应的特权级
就是RPL 第二个你执行这条指令
指令有个所谓的代码段
那么指令的代码段存在哪呢
CS里面 CS里面对应了一个CPL
就是当前的一个代码段的一个特权级
这个就是CPL 那么也意味着我们一条指令
要访问一个数据段的时候
它有两个特权级的表示
一个是当前的特权级CPL
位于CS的最低两位
第二个是对应的要访问的数据段的RPL
也是处于对应的ES或者FS等等
它那个低两位有个RPL
这两个占了两个bit和前面说的X86里面的
四级特权级是一一对应的
那就是0到3 0代表最高的优先级
数值越低特权级越高 数值越高特权级越低
正好是倒过来的 那这是发出请求的一方
另一方面我们说你要访问的那个段
无论是通过中断访问代码段
还是通过内存访问 访问数据段
它都会有一个那个段的描述
就是无论是门还是段
它都有一个所谓的DPL
这个DPL代表了
你要访问的目标它的特权级
这个在什么情况下
RPL CPL 和DPL处于什么关系的时候
这个访问是合法的
哪些情况是访问的不合法的
这是我们说CPU检查的时候
需要重点考虑的内容
其实这个检查是挺简单的
大家可以看看 有两种情况
一种是针对我们说的中断 陷入 异常
这种情况称之为门情况
访问门的时候 它需要做两个判断
它当前的代码段CPL要小于门所处于的DPL
也意味着这个门的特权级要比较低
而我当前执行的代码段要比较高
这样才允许通过门
第二个CPL要大于等于DPL
这什么意思呢 通过门之后目的是什么呢
是要我们去访问特权级更高的段
有了这个约束之后
其实就可以实现我们的应用程序
一般的特权级的应用程序
就是我们说处于ring 3特权级的应用程序
可以访问处于内核态的操作系统提供的服务
这属于特权级ring 0的这个操作系统的服务
就是从低优先级的可以访问高优先级的代码
这是说通过我们说的中断门
陷阱门或者陷入门可以完成这个功能
那么在我们后续的lab5中
有应用程序的时候就可以看到这一点
我们用户态的进程可以通过这种方式
通过系统调用的方式
来获得我们操作系统的服务
从用户态切换到内核态
后续也会给大家进一步的讲解
第二个是关于访问段的一个表述
对于访问段而言也一样
我们说我们当前的代码段
和我们要访问的这个数据段
我们发出的数据段特权级的请求
一定要小于等于DPL这个段
那么这意味着什么呢 这意味着说
当前发出请求的这一方
就是当前处于代码段执行这条指令
它发出请求 我当前处于这种状态
我要去访问某一个数据段
那么我本身的特权级要高于对应的目标
你作为使用方
我的特权级要比较高一点我才能访问
那么对应的数值的比较呢
说就是小于等于
就是CPL和RPL最大的一个值
就是最低那个权限
要高于它所对应的目标的那个段的特权级
我们这里面数值上是小
在实际特权级比较上是高
这点正好是倒过来的 所以是小于等于
那么有了这两个表示之后
如果说它能够通过这个检查
也就意味着它的特权级的检查是合格的
那么我们这个指令可以正常去访问
如果通不过就会产生所谓的保护错
也是一种异常