Answers:
在启动时,内核将初始化一个中断向量表(在x86上称为中断描述符表或IDT),该表指向每一行的中断处理程序。
在80286之前,IDT始终存储在固定地址中。从80286开始,使用LIDT
指令加载IDT 。
中断向量表指向每条中断线一个处理器。话虽这么说,内核可以选择提供运行其他几个中断例程的中断处理程序,或提供涵盖部分或全部中断的单个处理程序。Linux通过提供一个通用的中断处理程序来完成这些事情,该中断处理程序确定调用了哪个中断行并找到合适的下游处理程序。
是的,有一个预定义的位置,其中包含要跳转到的代码的地址:中断向量。根据处理器的不同,它可以是物理内存中的特定位置(8088),虚拟内存中的特定位置,处理器寄存器,由寄存器(ARM,386)指示的内存中的位置...
具体细节因处理器而异,但是处理处理器中的中断的主要共同要素是:
其他两个答案(在撰写本文时)讨论了中断和IDT。这是正确的,但是,在现代的Intel式CPU上,调用内核的方式不少于三种。
方法1:中断。
上面已经解释了这一点。您在中断描述符表/中断向量中设置了一个条目,然后执行软件中断以进入内核。
这种方法的主要优点是,典型的内核无论如何都需要能够处理中断,并且它可以在古老的硬件上运行。
方法2:呼叫闸口。
呼叫门是一种特殊的段选择器。呼叫目标需要加载到全局或本地段描述符表(分别为GDT和LDT)中。如果然后使用呼叫门作为段执行远距离呼叫指令(忽略呼叫偏移),则可以调用更多特权代码。呼叫门非常灵活;IA-32体系结构具有四个特权级别,而呼叫门可让您呼叫任何级别。
我不相信Linux曾经使用过呼叫门,但是Windows 95确实使用过。Win95内核服务(krnl386.exe
和kernel.dll
)实际上以用户模式(第3环)运行。最高特权级别(环0)仅用于驱动程序和仅执行进程切换的微内核。使用呼叫门完成对驱动程序的呼叫。这就允许传统的16位代码(其中有很多!)就像通常一样,仅通过标准的远距离调用就可以使用Win95驱动程序。
对全局描述符表的保护不足是导致多个Windows 95漏洞利用的原因,这些漏洞通过写入内存来设法安装自己的调用门。
方法3:SYSCALL / SYSRET和SYSENTER / SYSEXIT
这是两组指令,分别由AMD和Intel发明,但实际上它们执行相同的操作。SYSCALL / SYSRET首先出现,并且仅用于AMD,SYSENTER / SYSEXIT是Intel,但是AMD现在实现了它。因此,我将描述SYSENTER / SYSEXIT。
与呼叫门不同,SYSENTER只能用于转移到振铃0,并且只能转移到一个位置。但是,它具有极低的延迟优势,因为与调用或中断不同,它不会触及堆栈。
使用三个特定于模型的寄存器来设置传输位置:一个用于段信息,一个用于内核代码的指令指针和堆栈指针。因为没有东西“压入”堆栈,所以用户模式代码负责通过将返回指令指针和堆栈指针传递到寄存器中来告诉内核要返回的位置。内核负责还原堆栈指针,而SYSEXIT指令还原指令指针。