首先明確下概念:
在Intel的文檔中,把中斷分為兩種.一種是異常,也叫同步同斷或者內部中斷.一種稱之為中斷,也叫異常中斷或者外部中斷.
同步中斷指的是由CPU控制單元產生,之所以稱之為同步,是因為只有一條指令執行完畢後才會發出中斷.例如除法運算中,除數為零的時候,就會產生一個異常
非同步中斷是由外部裝置按照CPU的時鐘隨機產生的.例如,網卡檢測到一個資料到來就會產生一個中斷.
在init/main.c中:
asmlinkage void __init start_kernel(void)
{
……
//設定系統規定的異常/中斷,內部中斷初始化
trap_init();
//設定外部IRQ中斷,外部中斷初始化
init_IRQ();
……
}
在start_kernel中,調用trap_init()來設定系統規定的異常與中斷,調用init_IRQ()來設定外部中斷.
void __init trap_init(void)
{
……
set_trap_gate(0,÷_error);
set_intr_gate(1,&debug);
set_intr_gate(2,&nmi);
set_system_intr_gate(3, &int3); /* int3-5 can be called from all */
set_system_gate(4,&overflow);
set_system_gate(5,&bounds);
set_trap_gate(6,&invalid_op);
set_trap_gate(7,&device_not_available);
set_task_gate(8,GDT_ENTRY_DOUBLEFAULT_TSS);
set_trap_gate(9,&coprocessor_segment_overrun);
set_trap_gate(10,&invalid_TSS);
set_trap_gate(11,&segment_not_present);
set_trap_gate(12,&stack_segment);
set_trap_gate(13,&general_protection);
set_intr_gate(14,&page_fault);
set_trap_gate(15,&spurious_interrupt_bug);
set_trap_gate(16,&coprocessor_error);
set_trap_gate(17,&alignment_check);
#ifdef CONFIG_X86_MCE
set_trap_gate(18,&machine_check);
#endif
set_trap_gate(19,&simd_coprocessor_error);
set_system_gate(SYSCALL_VECTOR,&system_call); //系統調用
……
}
如上所示,設定了0~19的中斷/例外處理常式,這些都是intel所規定的,除些之後設定了系統調用入口(使用者空間的 int SYSCALL_VECTOR )
那, set_trap_gate()/set_intr_gate()/set_system_gata()都有一些什麼樣的區別呢?繼續看代碼:
void set_intr_gate(unsigned int n, void *addr)
{
_set_gate(idt_table+n,14,0,addr,__KERNEL_CS);
}
static inline void set_system_intr_gate(unsigned int n, void *addr)
{
_set_gate(idt_table+n, 14, 3, addr, __KERNEL_CS);
}
void __init set_trap_gate(unsigned int n, void *addr)
{
_set_gate(idt_table+n,15,0,addr,__KERNEL_CS);
}
void __init set_system_gate(unsigned int n, void *addr)
{
_set_gate(idt_table+n,15,3,addr,__KERNEL_CS);
}
都是通過統一的介面_set_gate().在i386中,這段代碼是用嵌入式彙編完成的,如下所示:
#define _set_gate(gate_addr,type,dpl,addr,seg)
do {
int __d0, __d1;
__asm__ __volatile__ ("movw %%dx,%%axnt"
"movw %4,%%dxnt"
"movl %%eax,%0nt"
"movl %%edx,%1"
:"=m" (*((long *) (gate_addr))),
"=m" (*(1+(long *) (gate_addr))), "=&a" (__d0), "=&d" (__d1)
:"i" ((short) (0x8000+(dpl<<13)+(type<<8))),
"3" ((char *) (addr)),"2" ((seg) << 16));
} while (0)
我們看可以看: _set_gate(gate_addr,type,dpl,addr,seg)中:
Gate_addr:相應IDT項的地址.type:設定IDT項的TYPE欄位, 15表示系統門,14表示中斷門.dpl:IDT項對應的DPL值,addr:中斷處理常式的地址,seg:IDT中對應項的段選擇符
由此可以看出,陷阱門與中斷門被鎖定在核心態(DPL為0),系統門可以從使用者態進入.那既然陷阱門與中斷門又有什麼區別呢?唯一的區別是,通過陷阱門不會改變FLAGES中的中斷標誌,不屏蔽外部中斷。但是中斷門就會改變,即會屏弊中斷
接下來看IRQ中斷的設定:
void __init init_IRQ(void)
{
int i;
//8259初始化
pre_intr_init_hook();
for (i = 0; i < (NR_VECTORS - FIRST_EXTERNAL_VECTOR); i++) {
//FIRST_EXTERNAL_VECTOR:第一個可用號,前面部份均為系統保留
int vector = FIRST_EXTERNAL_VECTOR + i;
if (i >= NR_IRQS)
break;
//跳過系統調用號
if (vector != SYSCALL_VECTOR)
set_intr_gate(vector, interrupt[i]);
}
……
……
}
舉例說明:在鍵盤中斷用陷井set_trap_gate(0x21,keyboard_interrupt),設定IRQ1作為鍵盤中斷。而不用set_intr_gate,這時因為如果在中斷處理的過程中中斷又來了的話,新的中斷還會被響應都存在緩衝區中,這是因為set_trap_intr不屏蔽外部中斷。
在串口初始化時:
void rs_init(void)
{
set_intr_gate(0x24,rs1_interrupt);//設定IRQ4作為串口一的中斷
set_intr_gate(0x23,rs2_interrupt);//設定IRQ3作為串口二的中斷
init(tty_table[1].read_q.data);//初始化串口,設定傳輸速率,開啟發送保持空以外的所有中斷等。
init(tty_table[2].read_q.data);
outb(inb_p(0x21)&0xE7,0x21);//允許8259響應IRQ3、IRQ4
}
串口設定中斷時用set_intr_gate,屏蔽外部中斷