我不完全理解你的问题。在 x86 CPU 上运行的现代操作系统中,没有真正的内核线程。操作系统大多是中断驱动的。
例如,在 Linux 上,您有 systemd,它作为内核的第一个进程运行。然后 systemd 查看它的“单元”,这些“单元”是允许指定接下来要运行的配置文件。这负责向您展示著名的用户 GUI 界面。所以你有一个目标文件,它指定了几个应该在系统启动时运行的单元。
在启动时,内核会初始化很多东西,然后启动 systemd。 systemd 进程本身是一个用户模式进程,它将在文件系统中查找单元文件的预定义目录。然后它将运行单位文件中指定的内容。由于 systemd 是用户模式进程,因此所有运行的进程都是用户模式进程。
它的工作原理是内核将为定时器中断设置处理程序。它将启动计时器,计时器将倒计时到 0。然后计时器将引发一个中断,该中断将启动一个处理程序。该处理程序将在内核中调用 schedule()。 schedule 函数将确定接下来要运行的进程并重新安排计时器。
不能有用户线程运行在一个内核线程之上,这需要一个内核线程始终运行。这是不可能的,尤其是在只有一个内核的系统上。
当您启动可执行文件时,您使用的 GUI 界面或命令行会在编译期间或启动前动态链接到的内核代码中进行系统调用。系统调用使用 sysenter 特殊 x86 指令跳转到特殊寄存器中指定的地址。启动后无法设置该寄存器。这提供了安全性,因此用户模式进程无法更改使用int 0x80 进行系统调用时将运行的指令。一旦进入内核模式,代码就可以访问所有硬件和所有指令。
我们这样做的目的是安全。因为用户模式进程不能仅仅决定破坏系统或运行它想要的任何内核代码。如果用户模式进程试图运行内核代码,它会受到 sysenter 指令的限制,该指令提供了从用户模式运行内核代码的软件接口。即使恶意的用户模式代码可以造成很大的危害,它也无法运行任何内核代码。
用户模式代码和内核模式代码之间的这种分离是在不同级别上完成的。您可以在页表中设置某些位,逐页指定访问级别。您也可以通过设置 IDT 中的某些位来指定中断的访问级别。所有这些都是由操作系统在启动时完成的。