乐趣区

保护模式特权级概述

在 IA32 的操作系统中,段被分为了 4 个特权级,分别为 0 - 3 级,有时候我们也叫做 ring0-ring3,其中,数值越小特权级越高。如下图所示:

图中,核心代码和数据所在的段的特权级都比较高,一般在 ring0,而用户程序所在的段的特权级较低,一般在 ring3。当低特权级的任务试图在未被允许的情况下访问高特权级的段时,将会产生常规保护错误。

而处理器是如何区分所在段的特权级,进而对其进行保护的呢?这就不得不提到 CPL、DPL 和 RPL 三者了。但是在开始之前,我们需要先了解一下一致代码段和非一致代码段。

一致代码段和非一致代码段

在操作系统中,我们有些高特权级的代码是希望被低特权级的程序所访问的,比如一些库函数,于是我们将这些高特权级代码放在一个叫做一致代码段的段里。而有些高特权级的代码,我们并不想让低特权级的程序所访问,于是我们把他们放在一个叫做非一致代码段的段里。具体来说,当通过 call 或者 jmp 指令转移到其它段时(即访问其他段),当转移的目标是一个优先级更高的一致代码段时,我们是可以进行访问的,但是当前的特权级会被延续下去;当转移的目标是一个优先级更高的非一致代码段时,这时的访问会引起常规保护错误(除非使用调用门或任务门)。

总结来说:

  • 一致代码段:由系统(高特权级)共享给低特权级的程序的代码所在的段,主要有下面两点限制:

    1. 高特权级程序不能访问低特权级的数据
    2. 低特权级的程序可以访问高特权级的代码,但是 特权级不会改变,还是保持低特权级程序的特权级
  • 非一致代码段:为了避免被低特权级程序所访问而保护起来的代码段,主要有一点限制:

    1. 只允许同级之间访问
  • 另外,数据段都是非一致的

所遵循的规则如下图所示:

CPL

CPL 全称 Current Privilege Level,顾名思义,CPL 代表的是当前执行的任务和程序的特权级。它存储在 cs 和 ss 的第 0 位和第 1 位上。一般情况下,CPL 等于代码所在段的特权级,当程序转移到不同的代码段时,处理器将改变 CPL 的值。

但是当访问一致代码段时,并不会改变 CPL,正如一致代码段中所说,一致代码段可以被低特权级的程序所访问,但是并不会改变特权级,所以也就不会改变 CPL,这就是与上面加粗的“一般情况”相对的“非一般情况”。

DPL

DPL 全程为 Descriptor Privilege Level,它代表段或者门的特权级。他被存储在段描述符或者门描述符属性中的 DPL 字段上。

当当前的代码段试图访问一个段或者门时,DPL 将会和 CPL 以及段或者门选择子的 RPL 相比较,而对于段或者门类型的不同,比较规则也不同,具体如下:

  • 数据段:如果访问的是一个数据段,DPL 代表访问此段的最低特权级,即只有 CPL <= DPL(数值上),才有权限访问。
  • 非一致代码段(不使用调用门):DPL 代表访问此段的特权级。即只有 DPL = RPL,才有权限访问。
  • 调用门:DPL 规定了当前执行的任务和程序可以访问此调用门的最低特权级,即要 CPL <= DPL
  • 一致代码段和非一致代码段(使用调用门访问):DPL 规定了访问此段的最高特权级。即只有 CPL >= DPL,才有权限访问。
  • TSS:DPL 规定了访问此 TSS 的最低特权级。

总结一下就是:

  • 在 数据段、调用门、TSS 中,DPL 代表最低特权级,访问要求:CPL <= DPL
  • 在 不使用调用门访问的非一直代码段时,访问要求:CPL = DPL
  • 在 一致代码段和使用调用门访问的非一致代码段中,DPL 代表最高特权级,访问要求:CPL >= DPL

RPL

RPL 全称是 Requested Privilege Level。RPL 保存在段选择子的第 0 位和第 1 位上。我们在上文说了当一个段访问另一个段时,CPL 与 DPL 的比较规则,但是仅仅比较 CPL 和 DPL 是不够的,处理器还要通过检查 RPL 来确定能否访问另一个段。

操作系统往往用 RPL 来避免低特权级应用程序访问高特权级段内的数据,即便提出访问请求的段有足够的特权级,如果 RPL 不够也是不行的,当 RPL 的值比 CPL 大的时候,RPL 将起决定性作用。也就是说,只有当 CPL 和 RPL 都小于要访问的数据段的 DPL 时,才有权限访问该数据段。


参考:

  • 《一个操作系统的实现》于渊
退出移动版