中断分类
对于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, ÷_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、是怎么走到中断处理函数的;