为什么x86分页没有特权级概念?

6
1982年英特尔发布了80286处理器,他们在分段方案中加入了4个权限级别(ring 0-3),由全局描述符表(GDT)和本地描述符表(LDT)中的2位指定。
在80386处理器中,英特尔添加了分页功能,但令人惊讶的是,它只有2个特权级别(监管者和用户),由页面目录表项(PDE)和页面表项(PTE)中的单个位指定。
这意味着仅使用分页的操作系统(如大多数现代操作系统)无法从1和2级特权级别的存在中获益,而这对于驱动程序可能非常有用。(例如,Win9x经常崩溃,因为它正在将带有错误的未经检查的驱动程序加载到ring 0中。)
从可移植性的角度来看,1和2级的存在是x86架构中的怪癖,并且可移植的操作系统不应该使用它们,因为其他架构只有2个特权级别。
但我相信,1985年设计386处理器时,英特尔工程师并没有考虑向其他平台的可移植性。
那么,为什么英特尔不像分段一样允许分页具有4个特权级别呢?

3
@user123 PDE和PTE只有一个比特位来指定权限。 - DarkAtom
@user123 是的,我是从CPU设计师的角度而不是操作系统设计师的角度在谈论。 - DarkAtom
你是对的,我记错了,页表只有2位指定访问级别。 - user123
1
我认为从CPU设计师的角度来看,他们之所以决定采用两个级别,是因为即使在分段存在的时候,操作系统也使用了两个级别。 - user123
1
@user123 在90年代之前,保护模式并没有得到广泛的应用,而当时386处理器已经问世5年了。因此,当英特尔设计386处理器时,他们不可能知道没有人会使用4个特权级别。 - DarkAtom
显示剩余4条评论
3个回答

3

在386保护模式和286中,有四个特权级别(称为环),第0个环具有最高特权级别(操作系统),1和2环不常用,第3个环具有最低特权级别(用户应用程序)。环0-2被称为“监管者”,而环3被称为“用户”。

当前特权级别(CPL)由您正在执行的指令的地址确定,根据代码段的描述符特权级别(DPL)。有关当前特权级别的更多信息,请参见CPL vs. DPL vs. RPL。

你所指的是以下内容。它是32位页目录条目(PDE)中的第2位,用于映射4MB页面(或32位PDE引用页面表)。此位称为“用户/监管员”(U/S)。该位中的“0”值表示不允许用户模式访问由该条目控制的4MB区域。这并不意味着只有“2个特权级别(监管员和用户)”,正如你所写的那样。 “监管员”级别仍然由三个环组成。这与用户环一起,总共有四个环。
请参阅Intel 64和IA-32体系结构软件开发人员手册第3A卷:系统编程指南第4.6节:
每个线性地址的访问都是监管员模式访问还是用户模式访问。对于所有指令获取和大多数数据访问,这种区分是由当前特权级别(CPL)确定的:当CPL< 3时进行监管员模式访问,而当CPL = 3时进行用户模式访问。
因此,CPL可以是0、1、2和3,实际上拥有所有4个环。
请从上面提到的手册中查找有关U/S标志的更多信息:
一些操作隐式地访问系统数据结构,使用线性地址[...]称为隐式监控模式访问,而不考虑CPL。在CPL<3时进行的其他访问称为显式监控模式访问。访问权限也由线性地址的模式控制,如控制线性地址翻译的分页结构条目所指定的那样。如果至少一个分页结构条目中的U/S标志(位2)为0,则该地址是监控模式地址。否则,该地址是用户模式地址。
附注:我的答案未解决为什么环1和环0之间没有与环3和环0/1/2之间相同的内存保护的问题,因此如果页面表项无法将它们与环0区分开来,则环1和2无法使用。请参阅彼得·科尔德斯的回复,解决了这个问题。

1
问题是为什么在第1环和第0环之间没有内存保护,就像在第3环和第0/1/2环之间一样。你并没有回答这个问题,只是详细说明了它的“缺失”。(该问题假设如果页面表项无法将第1环和第2环与第0环区分开来,则它们无法使用,而不是说它们根本不存在。) - Peter Cordes
@PeterCordes,谢谢你,我刚才提到了这个。 - Maxim Masiutin

3
我猜想英特尔的意图是,当Ring 1代码正在运行时,它就是"监督者",监督Ring 3代码。而不是Ring 1在Ring 0下运行。如果Ring 1代码想要调用Ring 0代码,它可以通过一个call-gate进行调用,Ring 0代码可以将CR3更改为包括在Ring 1或2代码所使用的页面表中不存在的物理页面的映射。我真的不太了解这些东西,但是https://wiki.osdev.org/Task_State_Segment显示TSS包含CR3字段,因此我猜测使用硬件任务切换,通过call-gate调用可以直接触发CR3更改。(因此调用目标本身不需要被映射,否则Ring 1 / 2代码可能已经对其进行了修改。或者它可以被映射为只读,连同页面表本身和GDT一起,以防止Ring 1代码通过修改来接管Ring 0。)
这意味着仅使用分页的操作系统无法从第1和第2个环中受益。您的错误在于:您不能“仅使用分页”。即使在具有平面内存模型的普通x86操作系统上使用户空间中断处理程序正常工作,也需要设置TSS内容以在切换到内核模式时将ESP设置为内核堆栈指针,即使您不使用硬件任务切换。x86具有“任务门”、“调用门”和各种非常复杂的东西,我希望我永远不必完全理解,但我期望花一些时间阅读可能会揭示386架构师认为操作系统可能想要做的事情的类型。与我之前的猜测(关于第1环监督第3环)分开,也许英特尔预计操作系统如果需要可以使用分段将第1/2环与第0环内存分开放置在同一个页表中。正如您所说,他们可能并没有试图创建可移植的微内核操作系统,只是作为额外的奖励。

内核有决定虚拟地址空间布局的特权,因此可以为使用ring 1代码分配该空间的块,并在调用时适当设置CS / DS / ES / SS。

我认为这必须意味着非平面模型,因为x86分段使地址从0..限制go from 0..limit,而不是允许访问低..高的一系列虚拟地址而不改变指针的含义。

注1

在ring 0和ring 1之间是否需要完整的内存保护?操作系统可能会将ring 1用于半可信代码。

一些特权指令需要在Ring 0级别执行,因此,Ring 1级别可以防止意外发生。IO特权级别可以单独设置,以允许在Ring > 0中使用cliin/out,但其他指令,如invlpg, lgdtmov cr, reg需要实际的Ring 0级别。


顺便说一下,@MichaelPetch或@IraBaxter,或其他有玩具操作系统/引导程序使用经验的用户可能会比我提供更好的答案,或填补我忽略的一些细节。Ira已经写了一些关于x86保护模式以及它如何实现类似Multics的内存模型的内容:“FS”/“GS”寄存器的作用是什么? - Peter Cordes
我从未进行过测试,但我非常确定一旦启用长模式,限制将被忽略,使分段对于内存保护无用。 - user123
@user123:是的,x86-64长模式中,除了FS/GS之外的段,基址=0且限长=-1已经被禁用。这个问答是关于英特尔386保护模式设计的,而不是AMD几十年后的AMD64设计。 - Peter Cordes
当然,这仅适用于更现代的CPU。 - user123
@user123:当然可以在“传统模式”下运行x86-64 CPU;只需不启用长模式并运行386操作系统即可。因此,即使是现代CPU也必须支持所有这些功能,只是不在长模式下。 - Peter Cordes
显示剩余3条评论

2
希望保护一些东西不被其他东西所侵害。在分页技术(paging)出现之前(以及80x86处理器出现之前——“四环”模型可以追溯到Multics或更早期),最简单的方法是使用“环”。通过4个环,你可以实现“D无法访问C,它们也无法访问B,同时它们都无法访问A”的安排。但是,相反的方向(“C可以访问D中的所有内容,而不管它是否需要”)和粒度(例如,“C可以访问D的一部分,但不能访问全部D”)都相对糟糕。
使用分页技术,你可以为每个东西提供自己的虚拟地址空间,并映射任何内容以控制访问权限(因为你无法访问未映射到你的虚拟地址空间中的任何内容)。你仍然可以实现“D无法访问C,它们也无法访问B,同时它们都无法访问A”(如果这是你真正想要的),只需将属于D的所有页面映射到A、B和C中;将属于C的所有页面映射到A和B中;等等。但是,你还可以有任何其他安排,例如模拟10个环而不是4个环,或让C访问D的一部分(但不是全部D)和B的一部分(但不是全部B),等等。
那么问题来了:如果仅使用分页就足以模拟任意数量的环(甚至更多),为什么我们仍然有2个环?
答案是,分页仅控制对已在内存中的内容(代码、数据)的访问,并且不能控制对未在内存中的内容(例如CPU的控制寄存器)的访问。因此,仍需要2个环来控制是否可以访问未在内存中的内容(例如,mov cr0, eax指令是否会导致通用保护故障)。
但是,有两件事情使这一点不太明显。切换不同的虚拟地址空间会带来一些成本,人们试图将其最小化(例如,不为共享库提供单独的虚拟地址空间,不为各个设备驱动程序提供自己的虚拟地址空间等)。另外,由于分页是添加到现有的“具有4个环的分段”设计中的(考虑向后兼容性),所以旧的“具有4个环的分段”设计的一些残留物仍在使用中(例如TSS、IO权限系统等)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接