Linux 内核在x86 中如何进入管理员模式?
How does the Linux kernel enter supervisor mode in x86?
我试图在发生模式切换(用户->内核模式)时探测事件,结果,我需要找到发生转换时将触发哪个函数。
看来SBI是RISC-V的过渡期。我想知道为 x86 处理此问题的代码在哪里?
没那么简单。在 x86 中,有 4 个不同的特权级别:0(操作系统内核)、1、2 和 3(应用程序)。 Linux 中未使用特权级别 1 和 2:内核运行在特权级别 0 而用户 space 代码运行在特权级别 3。当前特权级别 (CPL) 存储在位 0 和CS(代码段)寄存器的 1。
从用户到内核的转换有多种方式:
- 通过硬件中断:页面错误、一般保护错误、设备、硬件定时器等。
- 通过软件中断:
int
指令引发软件中断。 Linux中最常见的是int 0x80
,它被配置为用于用户space对内核space. 的系统调用
- 通过
sysenter
和 syscall
等专门指令。
在任何情况下,都没有执行转换的实际代码:它是由处理器本身完成的,它从一个特权级别切换到另一个特权级别,并设置段选择器、指令指针、堆栈指针等根据内核在启动后设置的信息。
在中断的情况下,Interrupt Descriptor Table (IDT) are used. See this useful documentation page about interrupts in Linux which explains more about the IDT. If you want to get into the details, check out Chapter 5 of the Intel 64 and IA-32 architectures software developer's manual, Volume 3.
的条目
简而言之,每个 IDT 条目指定一个描述符特权级别 (DPL) 以及一个新的代码段和偏移量。在软件中断的情况下,处理器会进行一些特权级别检查(其中之一是 CPL <= DPL)以确定发出中断的代码是否具有这样做的特权。然后,执行中断处理程序,隐式设置新的 CS 寄存器,并将特权级别位设置为 0。这就是 x86 32 位的规范 int 0x80
系统调用的方式。
对于像 sysenter
和 syscall
这样的专用指令,细节有所不同,但概念相似:CPU 检查权限,然后从专用的 Model Specific Registers (MSR) 之前由内核在引导后设置。
对于系统调用,结果总是相同的:用户代码切换到特权级别 0 并开始执行内核代码,在内核定义的不同系统调用入口点之一的开头结束。
可能的系统调用入口点是:
entry_INT80_32
对于 32 位 int 0x80
entry_INT80_compat
用于 32 位 int 0x80
在 64 位内核上
entry_SYSENTER_32
对于 32 位 sysenter
entry_SYSENTER_compat
用于 32 位 sysenter
在 64 位内核上
entry_SYSCALL_64
64 位 syscall
entry_SYSCALL_compat
for 32-bit syscall
on 64-bit kernel(用户代码不使用的特殊入口点,理论上 syscall
也是一个有效的 32 位AMD CPUs 上的指令,但 Linux 由于其奇怪的语义仅将其用于 64 位)
我试图在发生模式切换(用户->内核模式)时探测事件,结果,我需要找到发生转换时将触发哪个函数。
看来SBI是RISC-V的过渡期。我想知道为 x86 处理此问题的代码在哪里?
没那么简单。在 x86 中,有 4 个不同的特权级别:0(操作系统内核)、1、2 和 3(应用程序)。 Linux 中未使用特权级别 1 和 2:内核运行在特权级别 0 而用户 space 代码运行在特权级别 3。当前特权级别 (CPL) 存储在位 0 和CS(代码段)寄存器的 1。
从用户到内核的转换有多种方式:
- 通过硬件中断:页面错误、一般保护错误、设备、硬件定时器等。
- 通过软件中断:
int
指令引发软件中断。 Linux中最常见的是int 0x80
,它被配置为用于用户space对内核space. 的系统调用
- 通过
sysenter
和syscall
等专门指令。
在任何情况下,都没有执行转换的实际代码:它是由处理器本身完成的,它从一个特权级别切换到另一个特权级别,并设置段选择器、指令指针、堆栈指针等根据内核在启动后设置的信息。
在中断的情况下,Interrupt Descriptor Table (IDT) are used. See this useful documentation page about interrupts in Linux which explains more about the IDT. If you want to get into the details, check out Chapter 5 of the Intel 64 and IA-32 architectures software developer's manual, Volume 3.
的条目简而言之,每个 IDT 条目指定一个描述符特权级别 (DPL) 以及一个新的代码段和偏移量。在软件中断的情况下,处理器会进行一些特权级别检查(其中之一是 CPL <= DPL)以确定发出中断的代码是否具有这样做的特权。然后,执行中断处理程序,隐式设置新的 CS 寄存器,并将特权级别位设置为 0。这就是 x86 32 位的规范 int 0x80
系统调用的方式。
对于像 sysenter
和 syscall
这样的专用指令,细节有所不同,但概念相似:CPU 检查权限,然后从专用的 Model Specific Registers (MSR) 之前由内核在引导后设置。
对于系统调用,结果总是相同的:用户代码切换到特权级别 0 并开始执行内核代码,在内核定义的不同系统调用入口点之一的开头结束。
可能的系统调用入口点是:
entry_INT80_32
对于 32 位int 0x80
entry_INT80_compat
用于 32 位int 0x80
在 64 位内核上entry_SYSENTER_32
对于 32 位sysenter
entry_SYSENTER_compat
用于 32 位sysenter
在 64 位内核上entry_SYSCALL_64
64 位syscall
entry_SYSCALL_compat
for 32-bitsyscall
on 64-bit kernel(用户代码不使用的特殊入口点,理论上syscall
也是一个有效的 32 位AMD CPUs 上的指令,但 Linux 由于其奇怪的语义仅将其用于 64 位)