初始化中断描述表

现在,我们知道了80x86微处理器在硬件级对中断和异常做了些什么,接下来,我们可以继续描述如何初始化中断描述表。

内核启用中断以前,必须把IDT表的初始地址装到idtr寄存器,并初始化表中的每项。这项工作是在初始化系统时完成的。

int指令允许用户态进程发出一个中断信号,其值可以是0~255的任意一个向量。因此,为了防止用户通过int指令模拟非法的中断和异常,IDT的初始化必须非常小心。这可以通过把中断或陷阱门描述符的DPL字段设置成0来实现。如果进程试图发出其中的一个中断信号,控制单元将检查出CPL的值与DPL字段有冲突,并且产生一个”Generalprotection”异常。

然而,在少数情况下,用户态进程必须能发出一个编程异常。为此,只要把中断或陷阱门描述符的DPL字段设置成3,即特权级尽可能一样高就足够了。

现在,让我们来看一下Linux是如何实现这种策略的。

中断门、陷阱门及系统门

与在前面”中断描述符表”中所提到的一样,Intel提供了三种类型的中断描述符:任务门、中断门及陷阱门描述符。Linux使用与Intel稍有不同的细目分类和术语,把它们如下进行分类:

中断门

用户态的进程不能访问的一个Intel中断门。所有的Linux中断处理程序都通过中断门激活,并全部限制在内核态。

系统门

用户态的进程可以访问的一个Intel陷阱门。通过系统门来激活三个Linux异常处理程序,它们的向量是45128。因此,在用户态下,可以发布intoboundint$0x80三条汇编语言指令。

系统中断门

能够被用户态进程访问的Intel中断门。与向量3相关的异常处理程序是由系统中断门激活的,因此,在用户态可以使用汇编语言指令int3

陷阱门

用户态的进程不能访问的一个Intel陷阱门。大部分Linux异常处理程序都通过陷阱门来激活。

任务门

不能被用户态进程访问的Intel任务门。Linux对”Doublefault”异常的处理程序是由任务门激活的。

下列体系结构相关的函数用来IDT中插入门:

set_intr_gate(n,addr)

IDT的第n个表项插入一个中断门。门中的段选择符设置成内核代码的段选择符,偏移量设置成中断处理程序的地址addrDPL字段设置成0

set_system_gate(n,addr)

IDT的每n个表项插入一个陷阱门。门中的段选择符设置成内核代码的段选择符,偏移量设置成中断处理程序的地址addrDPL字段设置成0

set_system_intr_gate(n,addr)

IDT的第n个表项插入一个中断门。门中的段选择符设置成内核代码的段选择符,偏移量设置成中断处理程序的地址addrDPL字段设置成0

set_trap_gate(n,addr)

与前一个函数类似,只不过DPL的字段设置成0

set_task_gate(n,gdt)

IDT的第n个表项插入一个中断门。门中的段选择符存放一个TSS的全局描述符指针,该TSS中包含要被激活的函数,偏移量设置成0DPL字段设置成3


IDT的初步初始化

当计算机还运行在模式时,IDT被初始化并由BIOS例程使用。然而,一旦Linux接管,IDT就被移到RAM的另一个区域,并进行第二次初始化,因为Linux没有利用任何BIOS例程。

IDT存放在idt_table表中,有256个表项。6字节的idt_descr变量指定了IDT的大小和它的地址,只有当内核用lidt汇编指令初始化idtr寄存器时才用到这个变量。

在内核初始化过程中,setup_idt()汇编函数用同一个断门来填充所有这256idt_table表项。



用汇编语言写成的ignore_int()中断处理程序,可以看作一个空的处理程序,它执行下列动作:

  1. 在栈中保存一些寄存器的内容。

  2. 调用printk()函数打印”Unknowninterrupt”系统消息。

  3. 从栈恢复寄存器的内容。

  4. 执行iret指令以恢复被中断的程序。

ignore_int()处理程序应该从不被执行,在控制台或日志文件中出现的”Unknowninterrupt”消息标志着要么是出现了一个硬件问题,要么就是出现了一个内核的问题。

紧接着这个预初始化,内核将在IDT中进行第二遍初始化,用有意义的陷阱和断处理程序替换这个空处理程序。一旦这个过程完成,对控制单元产生的每个不同的异常,IDT都有一个专门的陷阱或系统门,而对于可编程中断控制器确认的每一个IRQIDT都将包含一个专门的中断门。

在接下来的两节中,将分别针对异常和中断来详细地说明这个工作是如何完成。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章