中断分类

对于Linux内核来说,中断信号通常分为两类:硬件中断和软件中断(异常)。每个中断由0~255之间的一个数字来标识。按照中断用途,可以分以下两类:

1、中断int0~int31(0x00~0x1f),每个中断的功能由Intel公司固定设定或保留用, 属于软件中断,但Intel公司称之为异常;因为这些中断是在CPU执行指令时探测到异常情况而引起的。通常还可分为故障(Fault)和陷阱(traps)两类。

2、中断int32~int255 (0x20~0xff)可以由用户自己设定,在Linux系统中,将int32~int47(0x20~0x2f)对应于8259A中断控制芯片发出的硬件中断请求信号IRQ0~IRQ15,并把程序编程发出的系统调用(system call)中断设置为int128(0x80)。

中断处理

intel CPU收到中断后,通过idt寄存器和中断向量找到相应的中断门函数,根据中断门描述执行一定的处理后(例如关闭中断等等),调用中断门函数,其中idt寄存器指向了中断门描述表的首地址,而中断向量即为表中的偏移。

中断初始化

异常类中断的中断门初始化在arch/x86/kernel/traps.c文件中的trap_init函数中实现,每个中断的中断门函数实现均不同;

void __init trap_init(void)
{
 set_intr_gate(0, &divide_error);
 set_intr_gate_ist(1, &debug, DEBUG_STACK);
 set_intr_gate_ist(2, &nmi, NMI_STACK);
 /* int3 can be called from all */
 set_system_intr_gate_ist(3, &int3, DEBUG_STACK);
 /* int4 can be called from all */
 set_system_intr_gate(4, &overflow);
 set_intr_gate(5, &bounds);
 set_intr_gate(6, &invalid_op);
 set_intr_gate(7, &device_not_available);
#ifdef CONFIG_X86_32
 set_task_gate(8, GDT_ENTRY_DOUBLEFAULT_TSS);
#else
 set_intr_gate_ist(8, &double_fault, DOUBLEFAULT_STACK);
#endif
 set_intr_gate(9, &coprocessor_segment_overrun);
 set_intr_gate(10, &invalid_TSS);
 set_intr_gate(11, &segment_not_present);
 set_intr_gate_ist(12, &stack_segment, STACKFAULT_STACK);
 set_intr_gate(13, &general_protection);
 set_intr_gate(14, &page_fault);
 set_intr_gate(15, &spurious_interrupt_bug);
 set_intr_gate(16, &coprocessor_error);
 set_intr_gate(17, &alignment_check);
#ifdef CONFIG_X86_MCE
 set_intr_gate_ist(18, &machine_check, MCE_STACK);
#endif
 set_intr_gate(19, &simd_coprocessor_error);
#ifdef CONFIG_IA32_EMULATION
 set_system_intr_gate(IA32_SYSCALL_VECTOR, ia32_syscall);
 set_bit(IA32_SYSCALL_VECTOR, used_vectors);
#endif
 set_system_trap_gate(SYSCALL_VECTOR, &system_call);
 set_bit(SYSCALL_VECTOR, used_vectors);
#endif
}

其他中断的中断门初始化在arch/x86/kernel/irqinit.c文件的native_init_IRQ函数中实现,每个中断对应的中断门函数是interrupt函数数组(每个函数实现均相同,最终调用common_interrupt函数)中的一项;

void __init native_init_IRQ(void)
{
 int i;
 /* Execute any quirks before the call gates are initialised: */
 x86_init.irqs.pre_vector_init();
 apic_intr_init();
 /*
  * Cover the whole vector space, no vector can escape
  * us. (some of these will be overridden and become
  * 'special' SMP interrupts)
  */
 for (i = FIRST_EXTERNAL_VECTOR; i < NR_VECTORS; i++) {
  /* IA32_SYSCALL_VECTOR could be used in trap_init already. */
  if (!test_bit(i, used_vectors))
   set_intr_gate(i, interrupt[i-FIRST_EXTERNAL_VECTOR]);
 }
 if (!acpi_ioapic)
  setup_irq(2, &irq2);
#ifdef CONFIG_X86_32
 /*
  * External FPU? Set up irq13 if so, for
  * original braindamaged IBM FERR coupling.
  */
 if (boot_cpu_data.hard_math && !cpu_has_fpu)
  setup_irq(FPU_IRQ, &fpu_irq);
 irq_ctx_init(smp_processor_id());
#endif
}

到此,中断门初始化已经完成,知道了CPU收到中断后的处理入口。还有几个疑问没有解答:

1、int32~int47(0x20~0x2f)对应硬件中断请求信号IRQ0~IRQ15是怎么做到的;

2、是怎么走到中断处理函数的;