写在代码前:
Jack:上次你说的8个问题。我都想过了。不过不是特别清楚。
我:那我从头介绍一下吧。
Jack:好。你开始吧。
中断在几乎所有的CPU里都分为三类:中断(interrupt)、异常(exception)、陷阱(trap)。在各种介绍CPU的书籍里,都会提到这三个概念,这里也就不复述了。如果想理解中断的本质大可以不去深究这3个概念,因为这三个概念完全是意淫出来的。
为了能将各种中断分门别类,CPU定义了一个“中断向量”(vector)的概念。而为了描述和管理中断向量,CPU有专门的寄存器——中断寄存器(IDTR)。IDTR里存储的是中断向量表的起始地址——听起来和全局描述寄存器(GDTR)、局部描述寄存器(LDTR)很类似吧。是的,中断描述符表IDT和GDT、LDT是同一个层面的东西。
x86的CPU定义了256种中断。其中0—19种是intel定义死了的,不能用作别的用途。而20—255是可以自己使用的。我们接触得最多的莫过于0x80号中断——所有的系统调用,如read、write、fork等,都是0x80号中断。
因为x86的CPU只有256种中断,所以,中断描述符表的entry数最多不大于256(可以小于256)。
Jack:中断描述符表的entry都记录了些什么呢?
我:中断描述符表里的entry分为三大类——中断(interrupt)门、陷阱(trap)门、任务(task)门。这里的“门”的意思很形象,就是说,通过这些entry就能进入到相应的中断服务程序对应的代码段。
Jack:那这些门是什么样子的呢?举个例子来分析下吧。
我:其中,任务门的格式和GDT、LDT中的entry的格式是一样的。三个门的格式具体如下:
摘自《Linux内核完全注释》
中断描述符表的内容、中断描述附表的地址一般在bootloader里就会设置好(地址存进idtr)。
只不过,这个时候,中断描述符表中的entry指向的代码都是空的,是“哑中断”。
这样,在进入start_kernel()函数之后,内核调用init_IRQ(void)进行真正的中断初始化。描述附表指向的程序才真正有效。