操作系统——特权级(十一)

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

操作系统——特权级(⼗⼀)
操作系统——特权级(⼗⼀)
2020-09-24 19:46:18 hawk 概述
这篇博客主要讲述⼀下特权级相关的知识,为后边内核的实现奠定基础。

这⾥也主要是相关的基础知识,如果已经有相关知识或者不太感兴趣的可以跳过,等需要的时候再回头看也可以。

特权级概述
特权级简介
实际上,整个计算机可以⼤体上分为两部分——访问者和受访者。

其中,访问者是动态的,其主动去访问各种资源,其特权是动态变化的;受访者是静态的,他就是被访问的资源,其特权应该是保持不变的。

⽽建⽴特权机制是为了通过特权来检查合法性,即主要发⽣在访问者去访问受访者的时候,检查内容就是访问者的特权级和受访者的特权级是否匹配。

操作系统中,特权级按照权⼒从⼤到⼩依次分为0、1、2、3级别。

操作系统位于0级特权,可以直接控制硬件,掌控各种核⼼数据;系统程序分别位于1级特权或2级特权,主要是⼀些虚拟机、驱动程序等系统服务;⽽⼀般的应⽤程序运⾏在3级特权。

TSS
特权级的实现和TSS密不可分。

TSS,即Task State Segment,任务状态段,是处理器在硬件上原⽣⽀持多任务的⼀种实现⽅式,主要⽤来存储任务的环境,其数据结构主题如下所⽰
这张图稍微有点长,因为TSS是⼀个⽐较重要的数据结构,每⼀个任务都有⼀个TSS,⽤于表⽰⼀个任务,相当于任务的⾝份证,并且程序拥有此结构才能正常运⾏。

⽬前来说,我们只需要关注低28字节即可。

可以看到,实际上这⾥⾯保存了三个栈指针。

这⾥稍微说明⼀下——任务是有处理器进⾏执⾏的。

因此任务在特权级变换时,实际上本质是处理器的当前特权级在变换,由⼀个特权级转换成了另⼀个特权级——实际上处理器在不同的特权级上,使⽤了不同特权级的栈(如果共⽤⼀个,⼀⽅⾯交叉引⽤会⼗分混乱;另⼀⽅⾯很可能栈溢出)。

并且每个任务在每个特权级下,仅仅有⼀个栈,也就是说,⼀个任务,最多有4个特权级。

相应的,其最多只有4个特权栈。

当然,我们会发现,实际上TSS中仅仅保留了三个栈。

实际上这和其⽤途有关。

实际上当处理器进⼊不同特权级栈后,其会前往TSS 中的对应的栈(因为硬件原⽣⽀持TSS功能的)。

⽽特权转移主要分为两类——⼀类是由中断门、调⽤门等⼿段的从低特权级转向⾼特权级;另⼀类是通过调⽤返回指令,从⾼特权级返回低特权级,这也是唯⼀⼀种能让处理器降低特权级的情况。

那么有趣的事情就发⽣了——除了调⽤返回指令外,处理器只能由低特权级向⾼特权级转移,TSS中只需要记录转移后的⾼特权级⽬标栈即可。

⽽如果我们在转移前将当前当前低特权级的栈地址(SS和ESP)压⼊转移后的⾼特权级的栈中,那么返回的时候,则可以将这些数据重新推⼊相关的寄存器中(可以通过调⽤retf和iret指令实现),从⽽⾃动的将栈从⾼特权级转移到低特权级栈,有点类似于call指令的返回。

这⾥还需要介绍⼀下,我们怎么找到TSS这个数据的地址呢?很简单,类似于gdtr等,现有软件完成构建,然后将其加载到相关的寄存器中即可,这⾥就是TR(Task Register)任务寄存器中即可。

CPL、DPL、RPL
CPL和DPL
前⾯我们⼀直在介绍特权机制,这⾥讲解⼀下具体的体现。

即DPL 、CPL以及RPL。

⾸先介绍⼀下CPL和DPL。

实际上在我们前⾯开启保护模式的过程中,实现了内存段寻址机制——段寄存器中保存的是选择⼦,通过选择⼦,从GDT或者LDT中找到相应的段描述符,然后从该段描述符中获取相关的段基址,和段偏移拼接,从⽽获取最终的地址。

⽽对于其中的段描述符来说,其低0-1位即表⽰RPL(Current Privilege Level)位,即请求特权级。

那么这⾥再详细说明⼀下——前⾯已经说过了,实际上计算机世界可以简单地划分为请求者和访问者。

对于访问者来说,其需要具有动态性,并且访问计算机资源,⾃然,这⾥只有指令才具备访问、请求其他资源的能⼒,即指令就是资源的请求者。

⽽指令请求、访问其他资源的能⼒等级便被称为请求特权级。

这⾥也就是通过cs代码段寄存器中选择⼦的RPL位,来表⽰代码请求别⼈资源的能⼒等级。

实际上,其也可以成为处理器的当前特权级,即CPL(Current Privilege Level)。

这⾥在额外说明⼀下,除⼀致性代码外,转移后的⽬标代码段的DPL,就是处理器的当前特权级CPL。

当然,如果我们从⼀个特权级的代码段转移到另⼀个特权级的代码段,由于两个代码段的特权级不⼀样,当处理器特权级检查通过后,其会将新的代码段的DPL替换当前代码段寄存器cs中的RPL位。

所以可以简单理解为,在任意时刻,当前特权级CPL始终保存在cs选择⼦中的RPL部分中。

那么另⼀个很重要的问题,初始的CPL是什么?很简单,就是0特权级。

因为从BIOS跳转到MBR时,执⾏的指令是jmp
0x0:0x7c00,并且⼀直到开启保护模式前,该cs段寄存器都基本没有发⽣过变化。

虽然开启保护模式前,说什么CPL是没有意义的;但是如果我们以保护模式机制分析⼀下相关的段寄存器,那么其CPL就是0。

⾃然,由于是最⾼特权级,⾃然后⾯的代码是应该正常执⾏的。

RPL和DPL
讲完了CPL和部分DPL,也就是访问者部分,下⾯我们说明⼀下另⼀部分——受访者。

实际上,受访者的特权标签是受访者所在段的段描述符的DPL。

受访者确保任何时候不允许⽐⾃⼰特权低的访问者进⾏访问,换⽽⾔之,就是访问者任何时候都不允许访问⽐⾃⼰特权更⾼的资源。

当然,还需要继续进⾏细分
1. 如果受访者是数据段,只有访问者的权限⼤于等于受访者的DPL表⽰的最低权限,才能继续正常的访问;
2. 如果访问者是代码段,只有访问者的权限等于受访者的DPL表⽰的最低权限,才能继续正常访问。

也就是仅仅允许平级访问。

很奇怪,为什么代码段仅仅允许平级访问,这⾥稍微说明⼀下。

⽬前的操作系统认为,正常cpu不会先⾃降等级然后再去实现某些功能(虽然我不是很认同)。

当然,会不会出现这种情况呢,肯定会啊,要不cpu怎么从操作系统转换到⽤户。

但是转换可能通过其他⼿段,⼏乎不会通过直接jmp或者call到⽤户代码来实现,所以这⾥仅仅允许平级也是可以理解的。

当然,前⾯也说了,既不不会,实际上有⼀种例外,处理器从中断处理程序中返回到⽤户态的时候。

这⾥就不细说了,只需要明⽩,除了从中断处理过程中返回外,任何时候执⾏代码对应的代码段,都不允许从⾼特权转移到低特权。

当然,我们接着前⾯的话题,如果代码段仅仅只能平级访问,⾃然是不可以的。

因此,实际处理器还提供了多种⽅式,⽤于⾮平级访问。

h
⾮平级访问
⼀致性代码段
这是⼀种既可以执⾏⾼特权级指令,同时⼜不提升CPL的额⽅式。

⼀致性代码段指的是对于转移后的⽬标段,⽬标段的特权级DPL,⼀定要⼤于等于转移前的CPL。

即必须在数值上有CPL >= DPL。

从⽽任何在⽬标段的权限下的特权级,都可以转移到此⽬标段上,执⾏相关的指令。

⽽⼀致性代码段的⼀⼤特点就是转移后的特权级不与⾃⼰的特权级(DPL)为主,也就是cpu遇到⽬标段是⼀致性代码段时,并不会将CPL⽤该⽬标段的DPL进⾏替换。

这⾥需要特殊说明⼀下,实际上仅仅只有代码段可以有⼀致性和⾮⼀致性的区分,⽽数据段总是⾮⼀致性的,即数据段不允许被⽐当前数据段特权级更低的代码段进⾏访问。

门、调⽤门与RPL
下⾯则是门结构。

实际上,处理器只有通过“门结构”,才能实现由低特权转移到⾼特权级别。

⽽门结构也很简单,就是记录⼀段程序起始地址的描述符⽽已。

实际上最开始介绍GDT的时候,⾥⾯的段描述符中我们也提到了相关的门,实际上如果我们将段描述符的特殊位进⾏设置,则该段描述符⼜可以被称为门描述符,其⼤体结构如下图所⽰
其中,所有的TYPE中D位为0表⽰16位模式,D位为1表⽰32位模式。

可以看到,实际上这些门描述符和段描述符最⼤的区别是除了任务门外,每⼀个门都对应到了⼀段例程,即⼀段函数上。

门描述符是基
于段描述符的,也就是对应的例程都是通过段描述符中的选择⼦和偏移量来获取相关例程的地址。

下⾯具体说明⼀下。

对于调⽤门来说,其通过call和jmp指令后接调⽤门选择⼦为参数,以调⽤函数例程的形式实现从低特权向⾼特权进⾏转移,可以⽤来实现系统调⽤。

其中call指令使⽤调⽤门可以实现向⾼特权代码进⾏转移;⽽jmp指令使⽤调⽤们仅仅只能实现平级代码转移。

对于中断门来说,以int指令主动发中断的形式,实现从低特权向⾼特权转移。

对于陷阱门来说,以int3指令主动发送中断的形式从低特权向⾼特权转移
对于任务门来说,任务切换通过中断或指令发起。

⽽当中断发⽣时,如果对应的中断向量号是任务门,则会发起任务切换。

也可以使⽤call或jmp指令接任务门的选择⼦进⾏调⽤。

下⾯仍然说⼀下门的适⽤范围——对于访问者来说,访问者的特权级不能⽐门描述符的特权级DPL低,即数值上CPL <= 门的DPL;除此之外,还要求访问者特权级的不能⽐门描述符中的⽬标程序所在代码段的DPL⾼,否则就是特权级从⾼到低转移,这是被禁⽌的。

当进⼊门后,处理器将以⽬标代码段的DPL作为当前特权级的CPL,也就是完成了特权级别的提升。

最后则是这个RPL,这⾥我看的⽐较绕,我先简单说明⼀下我的理解——RPL⽤来表⽰真正的资源访问者的特权级别的,否则如果不怀好意的⼈通过调⽤门等提升权限,会造成极⼤的破坏。

这⾥关于RPL,我还是⽐较困惑的,主要是其位置。

我理解的是这样的,如果执⾏如下指令
jmp sector:offset
就以这个例⼦说明⼀下RPL和CPL。

此时CPL也就是当前执⾏段的DPL;⽽RPL呢,实际上sector中低1-2位存储的就是RPL,这⾥实际上加载到CPU后,CPU会设置为对应的程序的CPL,⽤户是⽆法伪造的。

这⾥是RPL和CPL相同程序的例⼦。

实际上很可能对应的sector并不是当前程序提供的,可能是别的程序提供过来的,这⾥只是进⾏调⽤。

这样⼦,相当于RPL确实是真实请求者的特权级。

相关文档
最新文档