3.2.2中断描述符表IDT的预初始化

   当计算机运行在实模式时,IDT被初始化并由BIOS使用。然而,一旦真正进入了Linux内核,IDT就被移到内存的另一个区域,并进行进入实模式的初步初始化。

 1.中断描述表寄存器IDTR的初始化

     用汇编指令LIDT对中断向量表寄存器IDTR进行初始化,其代码在arch/i386/boot/setup.S 中:

 

lidt    idt_48                   # load idt with 0,0

    idt_48:

         .word   0                  # idt limit = 0

         .word   0, 0                # idt base = 0L

  2.把IDT表的起始地址装入IDTR

   用汇编指令LIDT装入IDT的大小和它的地址(在arch/i386/kernel/head.S中):

 

#define IDT_ENTRIES     256

.globl SYMBOL_NAME(idt)

 

lidt idt_descr

idt_descr:

        .word IDT_ENTRIES*8-1           # idt contains 256 entries

SYMBOL_NAME(idt):

         .long SYMBOL_NAME(idt_table)

    其中idt为一个全局变量,内核对这个变量的引用就可以获得IDT表的地址。表的长度为256´82048字节。

3.用setup_idt()函数填充idt_table表中的256个表项。

   我们首先要看一下idt_table的定义(在arch/i386/kernel/traps.c中):

 

   struct desc_struct idt_table[256] __attribute__((__section__(".data.idt"))) = { {0, 0}, };

 

   desc_struct结构定义为:

 

   struct desc_struct

    unsigned long a,b   }

 

   idt_table变量还定义了其属性(__attribute__),__section__是汇编中的“节”,指定了idt_table的起始地址存放在数据节的idt变量中,如上面第2条所述。

 

  在对idt_table表进行填充时,使用了一个空的中断处理程序ignore_int()。因为现在处于初始化阶段,还没有任何中断处理程序,因此用这个空的中断处理程序填充每个表项。ignore_int()是一段汇编程序(在head.S中):

 

ignore_int:

        cld            #方向标志清0,表示串指令自动增长它们的索引寄存器(esiedi

        pushl %eax

        pushl %ecx

        pushl %edx

        pushl %es

        pushl %ds

        movl $(__KERNEL_DS),%eax

        movl %eax,%ds

        movl %eax,%es

        pushl $int_msg

        call SYMBOL_NAME(printk)

        popl %eax

        popl %ds

        popl %es

        popl %edx

        popl %ecx

        popl %eax

        iret

int_msg:

       .asciz "Unknown interrupt\n"

        ALIGN

 

该中断处理程序模仿一般的中断处理程序,执行如下操作:

·      中保存一些寄存器的值

·      调用printk()函数打印“Unknown interrupt”系统信息

·      中恢复寄存器的内容

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

 

实际上,ignore_int()处理程序应该从不执行。如果在控制台或日志文件中出现了 “Unknown interrupt”消息,说明要么是出现了一个硬件问题(一个I/O设备正在产生没有预料到的中断),要么就是出现了一个内核问题(一个中断或异常未被恰当地处理)。

 

  最后,我们来看setup_idt()函数如何对IDT表进行填充:

 

 /*

  *  setup_idt

  *

  *  sets up a idt with 256 entries pointing to

  *  ignore_int, interrupt gates. It doesn't actually load

  *  idt - that can be done only after paging has been enabled

  *  and the kernel moved to PAGE_OFFSET. Interrupts

  *  are enabled elsewhere, when we can be relatively

  *  sure everything is ok.

  */

setup_idt:

         lea ignore_int,%edx   /*计算ignore_int地址的偏移量,并将其装入%edx*/

         movl $(__KERNEL_CS << 16),%eax  /* selector = 0x0010 = cs */

         movw %dx,%ax           

         movw $0x8E00,%dx        /* interrupt gate - dpl=0, present */

 

         lea SYMBOL_NAME(idt_table),%edi

         mov $256,%ecx

rp_sidt:

         movl %eax,(%edi)

         movl %edx,4(%edi)

         addl $8,%edi

         dec %ecx

         jne rp_sidt

         ret

 

    这段程序的理解要对照门描述符的格式。8个字节的门描述符放在两个32位寄存器eaxedx,如图3.5所示,从rp_sidt开始的那段程序是循环填充256个表项。

           32                   16 15                               0

        eax

 

                                  

3.4 门描述符存放在两个32位的寄存器