在Linux内核以及许多在线x86教程中,我看到人们建议使用两个代码段和两个数据段。我了解需要两个代码段,因为CPL需要与DPL完全匹配(对于不合格的段)。

但是,这些教程都没有(也不涉及StackOverflow的任何相关问题),也没有特别说明为什么我们需要两个数据段。它们的工作方式与代码段不同,因为CPL = 0的进程可以访问DPL = 3的数据段。

如果我们在不同特权级别的进程之间进行切换,则具有两个数据段的缺点是必须重新加载DS,ES等寄存器。

所以我的具体问题是:假设我们使用的是平面内存模型,那么所有代码​​和段都完全重叠,那么拥有一个用户和一个内核数据段(而不是一个用户数据段)有什么目的?

最佳答案

有一个解释here

引用英特尔手册(第5.7节)


当SS寄存器加载了堆栈段的段选择器时,也会进行特权级别检查。
此处,与堆栈段相关的所有特权级别必须与CPL匹配;也就是说,CPL,堆栈段选择器的RPL和堆栈段描述符的DPL必须相同。如果RPL和DPL不相等
对于CPL,将生成一个通用保护异常(#GP)。


重点矿

也就是说,SS需要从内核加载时(或在切换期间)DPL等于0的数据段。
对于32位模式,这是正确的。

在64位模式下,it is possible to use a NULL selector禁止任何运行时检查(包括上一个检查)1


在64位模式下,处理器不对NULL段选择器执行运行时检查。处理器确实
尝试访问引用的段寄存器具有以下内容的内存时,不会导致#GP错误
空段选择器。


出于完整性考虑,在执行堆栈操作时,所有相关信息,地址大小,操作数大小和堆栈地址大小都将从代码段中恢复,或者隐式设置为64位。



1如果我没记错的话,出于兼容性原因,64位模式仍然使用内核数据段。

09-12 10:13