当前位置:首页 > > IOT物联网小镇
[导读]作 者:道哥,10年的嵌入式开发老兵,专注于:C/C、嵌入式、Linux。关注下方公众号,回复【书籍】,获取Linux、嵌入式领域经典书籍;回复【PDF】,获取所有原创文章(PDF格式)。目录CPL:当前特权级DPL:描述符特权级RPL:请求者特权级特权级检查规则代码段的检查规则...

作  者:道哥,10 年的嵌入式开发老兵,专注于:C/C 、嵌入式、Linux


关注下方公众号,回复【书籍】,获取 Linux、嵌入式领域经典书籍回复【PDF】,获取所有原创文章( PDF 格式)


目录


  • CPL:当前特权级


  • DPL:描述符特权级


  • RPL:请求者特权级


  • 特权级检查规则


    • 代码段的检查规则


    • 数据段的检查规则


    • 栈段的检查规则



x86处理器中,提供了4个特权级别:0,1,2,3。数字越小,特权级别越高!


Linux从头学12:读完这篇【特权级】文章,你就比别人更“精通”操作系统!一般来说,操作系统是的重要性、可靠性是最高的,需要运行在0 特权级;


应用程序工作在最上层,来源广泛、可靠性最低,工作在3 特权级别。


中间的1 和 2两个特权级别,一般很少使用。


理论上来讲,可以把那些可靠性介于操作系统和应用程序之间的程序安排在这两个特权级上。


在处理器中,有3个相关的术语与特权级密切相关:


  1. CPL: Current Privilege Level 当前特权级;


  2. DPL: Descriptor Privilege Level 描述符特权级;


  3. RPL: Requestor Privilege Level 请求特权级;


理解了这3个特权级的保护规则,就理解了操作系统保护系统的终极密码!


CPL:当前特权级

当前特权级,是指当前正在执行的代码的特权级别,它由当前正在执行的代码段寄存器cs中的bit[1 ~ 0]来决定:


Linux从头学12:读完这篇【特权级】文章,你就比别人更“精通”操作系统!cs寄存器中的最低两位怎么写的是RPL?


原因是这样的:我们在执行一段代码之前,这段代码位于内存中的一段空间中,而它的代码段描述符位于LDT局部描述符表中,如下图所示:


Linux从头学12:读完这篇【特权级】文章,你就比别人更“精通”操作系统!假设现在想进入这个代码段中执行,那么我们就需要给代码段寄存器 cs赋值为:0x0007 (0000_0000_0000_0111)。


此时cs寄存器中当前内容的低两位就称作:当前优先级,而准备赋值给cs的值是0x0007,这个0x0007就称作选择子


按照选择子的结构来解析:


  1. RPL: bit[1 ~ 0] = 11B,十进制就是 3,就表示这个选择子的请求特权级别是 3;


  2. TI: bit[2] = 1B,表示到 LDT 中查找段描述符;


  3. 索引号:bit[15 ~ 3] 的索引值为 0,表示到 LDT 中偏移量为 0 (0 = 0 * 4, 每个描述符占据 4 个字节) 的位置获取段描述符;


当处理器进行一系列权限检查之后,允许进入这段代码中去执行,那么就设置cs = 0x0007。


此时cs寄存器中的最低2位就等于选择子中的RPL,也就是3。


在一般情况下,CPL都是等于RPL的。


DPL:描述符特权级

DPL指的是一个段描述符中,用来指定这个描述符所代表的段,具有什么样的特权级别


关于描述符的结构,如下图所示:


Linux从头学12:读完这篇【特权级】文章,你就比别人更“精通”操作系统!bit[14 ~ 13]就表示这个段描述符的特权级别。


当请求访问一个段时(不论是数据段,还是代码段),处理器在GDT或者LDT中找到段描述符之后,就会把CPL、RPL与描述符中的DPL进行比较。只有满足一定的规则,才允许访问这个描述符所指向的那个段。


具体的比较规则,下文有描述。


RPL:请求者特权级

刚才的CPL内容中,已经描述了RPL是什么东西,它俩是密切相关的。


但是,有时候 CPL 与 RPL 并不相同


比如:


一个用户程序,想通过操作系统提供的系统函数,去访问内存中的一块用户程序自己的内存空间(数据段)。


用户程序需要告诉操作系统:访问哪一个数据段,偏移量是多少。


这些信息需要把一个选择子通过操作系统来赋值给数据段寄存器 ds


假设选择子是0x000F(二进制:0000_0000_0000_1111):


索引号:1;


TI: 使用 LDT;


RPL: 3;


也就是说:当操作系统接受用户程序的请求之后,开始执行系统函数时,此时的CPL是操作系统的特权级别0。


此时操作系统需要把一个选择子赋值给数据段寄存器 ds,而这个选择子是由用户程序作为参数传递给操作系统的。


在这个场景中:CPL = 0, RPL = 3,它俩就不相等。


操作系统用这个选择子0x000F到用户程序的LDT中,根据索引号1找到数据段描述符之后,把CPL(0)、RPL(3)与描述符中的DPL进行比较,来判断是否有权限访问这个数据段。


  1. 用户程序的数据段 DPL 一定是 3,这是由操作系统在加载程序之初就决定好的;


  2. 根据下文的特权级检查规则,这样的访问是允许的;


其实这里有一个隐患


假如用户程序是一个恶意程序,它想破坏操作系统的数据,于是就传入一个指向操作系统数段的选择子:0x0010(二进制:0000_0000_0001_0000):


索引号:2(假设通过其它途径,知道操作系统的某个数据段位于 GDT 的第 2 个表项);


TI: 使用 GDT;


RPL: 0;


此时,如果操作系统很无脑的就原样接收了用户程序的调用请求,就会通过GDT找到属于操作系统的数据段进行破坏性操作。


操作系统不会这么傻的,它在接收用户程序请求的时候,会严格检查用户程序传入的参数


如果它发现运行在3 特权级的用户程序,传入一个0 特权级的 RPL,就会警觉:请求特权级竟然比你自己的运行特权级还高,你想干什么?


于是,操作系统就会把选择子中的RPL修改为用户程序的当前特权级CPL。


就好比:一个村长去找市长办事,诉求是:想在自己村的集体土地上盖一座厂房。市长认为:这是你们村自己的土地,你可以随便折腾,准许。


但是,如果村长的诉求是:想在市民广场的旁边盖一座厂房。此时市长就会呵斥:这个地方不是你们村的一亩三分地,想干啥就干啥,滚开!


特权级检查规则

代码段的特权级检查

一般情况下,只允许两个特权级相同的代码段进行转移。


例如:


  1. 从用户程序的一个代码段(CPL = 3),跳转到另一个 DPL = 3 的代码段;


  2. 从操作系统的一个代码段(CPL = 0),跳转到另一个 DPL = 0 的代码段;


但是处理器也提供了一些特殊途径,让特权级的代码可以转移到特权级的代码中去执行:


  1. 如果在高特权级代码段描述中的 TYPE 字段中,C = 1,就允许低特权级的代码转移进来;


  2. 通过调用门,低特权级代码也可以转移到高特权级的代码段;


这里主要描述第一种情况,也就是当目标代码段描述符的TYPE字段中C = 1,也就是所谓的依从代码,或者一致性代码


Linux从头学12:读完这篇【特权级】文章,你就比别人更“精通”操作系统!也即是说:如果TYPE.C = 1,那么处理器就允许:比这个描述符的 DPL 更低特权级的代码,转移到这个代码中来执行。


在数值上就是:(特权级越低,数值越大)


CPL >= DPL


RPL >= DPL


例如:操作系统中有2个代码段,它们的描述符中的C标志位不同:


Linux从头学12:读完这篇【特权级】文章,你就比别人更“精通”操作系统!此刻正在执行一个用户程序:CPL = 3。


那么用户程序就可以转移到代码段 2中去执行,不可以转移到代码 1中。


并且,转移到操作系统的代码段2 之后,当前特权级CPL保持不变,仍然为3。


有两个类比:


1. 类似于 Linux 中的 sudo 指令


如果一条指令需要root权限,我们可以使用su -指令,把身份转换到root,然后再去执行。


此时所有的身份、环境变量等信息,都是root用户的。


我们还可以直接使用sudo指令,这时就相当于是临时提升了用户的权限,但是那些环境变量等信息,依然是当前用户的,而不是 root 用户的


2. 村长找市长办贷款


村长去市里的银行申请贷款,但是自己的权力不够,银行不鸟他,于是村长就去找市长帮忙。


于是,市长就给村长一个亲笔介绍信,村长带着这封信到银行之后,银行一看:有市长大人的背书,于是就给村长办理贷款手续了。


但是,在办理手续的过程中,所有需要签字的地方,只能写村长自己(特权级不变),而不能写市长的名字


另外,对于上图中的代码段1,由于其C标志位是0,只能允许相同特权级的程序转移进来,从数值上表示就是:


CPL == DPL


RPL == DPL


最后还有一点需要记住:高特权级的代码,永远都不能转移到低特权级的代码。就好比:市长永远都不会以村长的身份去办事。


数据段的特权级检查

数据段的特权级检查规则比较简单:高特权级的程序,可以访问低特权级的数据,反之不可以


从数值上表示就是:


CPL <= DPL


RPL <= DPL


栈段的特权级检查

栈段的特权级检查规则,也比较简单,x86处理器要求当前特权级 CPL 必须与目标栈段的 DPL 相同。


从数值上表示就是:


CPL == DPL


RPL == DPL


为了满足这个要求,当从用户程序(CPL = 3)转移到操作系统(DPL = 0)时,如果是通过依存(一致性)代码段转移进去,当前特权级是不变的,此时使用的栈仍然是用户程序的栈空间


如果是通过其他途径转移进去(eg: 调用门),当前特权级发生了变化(CPL = 0),此时使用的栈就必须是 0 特权级下的栈空间了。


因此,操作系统在加载这个用户程序的时候,就需要提前申请一块栈空间,以准备在以上这样的场景中使用。


在Linux系统中,只用了0 和 3这两个特权级,因此每一个用户程序只需要提前准备好0特权级下使用的栈就可以了。


如果一个操作系统使用了0 ~ 3所有的四个特权级,那么操作系统就必须为:运行在3特权级下的用户程序准备3个栈空间,用于该用户程序转移到特权级0、1、2下作为栈空间来使用。



------ End ------
这篇文章主要从特权级的角度,来理解操作系统对系统的保护。


在这样的机制下,操作系统很好的保护了系统不被恶意程序破坏,同时也为用户程序的执行提供了一些通道,来调用更底层的功能。


本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: 驱动电源

在工业自动化蓬勃发展的当下,工业电机作为核心动力设备,其驱动电源的性能直接关系到整个系统的稳定性和可靠性。其中,反电动势抑制与过流保护是驱动电源设计中至关重要的两个环节,集成化方案的设计成为提升电机驱动性能的关键。

关键字: 工业电机 驱动电源

LED 驱动电源作为 LED 照明系统的 “心脏”,其稳定性直接决定了整个照明设备的使用寿命。然而,在实际应用中,LED 驱动电源易损坏的问题却十分常见,不仅增加了维护成本,还影响了用户体验。要解决这一问题,需从设计、生...

关键字: 驱动电源 照明系统 散热

根据LED驱动电源的公式,电感内电流波动大小和电感值成反比,输出纹波和输出电容值成反比。所以加大电感值和输出电容值可以减小纹波。

关键字: LED 设计 驱动电源

电动汽车(EV)作为新能源汽车的重要代表,正逐渐成为全球汽车产业的重要发展方向。电动汽车的核心技术之一是电机驱动控制系统,而绝缘栅双极型晶体管(IGBT)作为电机驱动系统中的关键元件,其性能直接影响到电动汽车的动力性能和...

关键字: 电动汽车 新能源 驱动电源

在现代城市建设中,街道及停车场照明作为基础设施的重要组成部分,其质量和效率直接关系到城市的公共安全、居民生活质量和能源利用效率。随着科技的进步,高亮度白光发光二极管(LED)因其独特的优势逐渐取代传统光源,成为大功率区域...

关键字: 发光二极管 驱动电源 LED

LED通用照明设计工程师会遇到许多挑战,如功率密度、功率因数校正(PFC)、空间受限和可靠性等。

关键字: LED 驱动电源 功率因数校正

在LED照明技术日益普及的今天,LED驱动电源的电磁干扰(EMI)问题成为了一个不可忽视的挑战。电磁干扰不仅会影响LED灯具的正常工作,还可能对周围电子设备造成不利影响,甚至引发系统故障。因此,采取有效的硬件措施来解决L...

关键字: LED照明技术 电磁干扰 驱动电源

开关电源具有效率高的特性,而且开关电源的变压器体积比串联稳压型电源的要小得多,电源电路比较整洁,整机重量也有所下降,所以,现在的LED驱动电源

关键字: LED 驱动电源 开关电源

LED驱动电源是把电源供应转换为特定的电压电流以驱动LED发光的电压转换器,通常情况下:LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: LED 隧道灯 驱动电源
关闭