Linux0.12的中断处理过程源码分析
在linux0.12中和中断处理文件有asm.s和traps.c。asm.s用于实现大部分硬件异常所引起的中断服务过程的汇编程序部分,而traps.c程序实现asm.s中段处理过程中调用的c函数部分。中断服务由内核提供,因此中断处理过会使用进程的内核态栈。
关于IDB的定义和初始化相关代码如下,首先是在启动代码boot/head.S中定义了一个idt表并将其全部设置了一个默认的中断处理函数:
.align 3
_idt: .fill 256,8,0 # idt is uninitializedsetup_idt:
/** %eax:* 位31-16: 0x0008 (段选择子)* 位15-0: ignore_int地址的低16位* %edx:* 位31-16: 0x8E00 (中断门属性)* 位15-0: ignore_int地址的高16位
*/lea ignore_int,%edxmovl $0x00080000,%eaxmovw %dx,%axmovw $0x8E00,%dxlea _idt,%edi # edi指向IDT的基地址mov $256,%ecx # 256个中断门
rp_sidt:movl %eax,(%edi)movl %edx,4(%edi)addl $8,%edi # 移动到下一个中断门描述符dec %ecx # 循环计数器减1jne rp_sidtlidt idt_descr # 加载IDT描述符ret
然后在kernel/traps.c中重新设置了部分中断的处理函数
void trap_init(void)
{int i;set_trap_gate(0,÷_error);set_trap_gate(1,&debug);set_trap_gate(2,&nmi);set_system_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_trap_gate(8,&double_fault);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_trap_gate(14,&page_fault);set_trap_gate(15,&reserved);set_trap_gate(16,&coprocessor_error);set_trap_gate(17,&alignment_check);for (i=18;i<48;i++)set_trap_gate(i,&reserved);set_trap_gate(45,&irq13);/* 配置8259A可编程中断控制器,启用特定中断 */outb_p(inb_p(0x21)&0xfb,0x21);outb(inb_p(0xA1)&0xdf,0xA1);set_trap_gate(39,¶llel_interrupt);
}
中断处理函数的定义是在汇编代码kernel\asm.s中,当中断被触发后,CPU会自动的在IDT表里面根据中断号找到对应的中断处理函数。以除0异常举例,CPU将会跳转到_divide_error开始执行(关于_do_divide_error的定义是在kernel\trps.c中)
_divide_error:pushl $_do_divide_error # 将do_divide_error的地址压入栈中
no_error_code:xchgl %eax,(%esp) # 将函数地址从栈移到eax# 保存所有通用寄存器和段寄存器,为调用C函数做准备pushl %ebxpushl %ecxpushl %edxpushl %edipushl %esipushl %ebppush %dspush %espush %fspushl $0 # 将0压入栈中,占位符lea 44(%esp),%edx # edx指向异常发生时的栈指针位置pushl %edx # 将edx入栈,作为第一个参数传递给C函数# 切换到内核数据段,异常处理需要访问内核数据movl $0x10,%edxmov %dx,%dsmov %dx,%esmov %dx,%fscall *%eax # 间接调用eax中的函数地址addl $8,%esp # 将栈指针向上移动8字节,这8字节是pushl $0和pushl %edx# 恢复段寄存器和通用寄存器pop %fspop %espop %dspopl %ebppopl %esipopl %edipopl %edxpopl %ecxpopl %ebxpopl %eaxiret # 中断返回