为什么Linux有两个数据段,一个用于用户态,另一个用于内核态?

为什么Linux有两个数据段,一个用于用户态,另一个用于内核态?

Linux(除了其他段之外)还有用户数据段和内核数据段。

当CPU处于用户模式时,Linux将用户数据段的段选择器加载到DS寄存器中。而当CPU处于内核模式时,Linux将内核数据段的段选择器加载到DS寄存器中。

但是内核数据段仍然可以从用户模式访问,因为用户数据段和内核数据段指向相同的虚拟内存地址!

那么为什么Linux有两个数据段,一个用于用户态,另一个用于内核态呢?

答案1

Linux 对 SS(堆栈段)和 DS(数据段)使用相同的段描述符。 SS段描述符必须有一个与CPL(即当前特权级别)完全相等的DPL字段。因此,内核和用户模式需要单独的数据段描述符。

资料来源(我懒得下载CPU手册)

定义中的注释gdt_page:

我们还需要长模式下数据和代码的有效内核段。 IRET 将检查段类型。 ——凯尔 2000/10/28

网络搜索:iret 检查段类型

阿布舍克·亚达夫:

将选择器加载到 SS 的指令必须引用可写数据段的数据段描述符。描述符特权 (DPL) 和 RPL 必须平等的CPL。所有其他描述符类型或特权级别违规都将导致异常 13。

网页搜索:iret 检查段类型 OR“DPL”OR“CPL”“SS”

“很多但有限”:

...堆栈段寄存器 ss 除外,CPL、RPL 和 DPL 三个必须匹配确切地

bochs-2.6.2/cpu/iret.cc:256:

    /* stack segment DPL must equal the RPL of the return CS selector,
     * else #GP(SS selector) */
    if (ss_descriptor.dpl != cs_selector.rpl) {
       BX_ERROR(("iret: SS.dpl != CS selector RPL"));
       exception(BX_GP_EXCEPTION, raw_ss_selector & 0xfffc);
    }

背景:为什么Linux有单独的代码用户模式和内核模式的段?

相关,另一条评论内核源代码:

由于 DPL 不同,我们不能在用户模式和内核模式中使用相同的代码段描述符,即使在长平面模式下也是如此。

这是因为当前代码段的DPL用作CPL

我注意到 32 位和 64 位代码还需要不同的代码段 -

https://en.wikipedia.org/wiki/Segment_descriptor-

L=长模式段

如果设置,这是一个 64 位段(并且 D 必须为零),并且该段中的代码使用 64 位指令编码


我猜 DS 段寄存器在从用户空间进入内核时会被重置,至少在 x86-32 上是这样。但我无法识别执行此操作的代码。

还有一个最近的 LWN.net 文章,其中对 set_fs() 做出了有趣的评论。

set_fs()最初的作用是设置x86处理器的FS段寄存器,早期用于控制非特权代码可以访问的虚拟地址范围。内核……早已停止以这种方式使用 x86 段了。

相关内容