《linux内核完全剖析》笔记02-中断处理

来源:互联网 发布:在线网络理财规划 编辑:程序博客网 时间:2024/05/17 12:03

1. 从硬件故障中断处理理解

要理解中断系统从理解一段代码开始

no_error_code:    xchgl %eax,(%esp)       #将eax的内容和esp所指堆栈的内容相交换    pushl %ebx    pushl %ecx    pushl %edx    pushl %edi    pushl %esi    pushl %ebp    push %ds    push %es    push %fs    pushl $0        # 第二个参数    lea 44(%esp),%edx        pushl %edx      #第一个参数    movl $0x10,%edx #转到内核态    mov %dx,%ds    mov %dx,%es    mov %dx,%fs    call *%eax      #调用中断处理函数    addl $8,%esp    pop %fs    pop %es    pop %ds    popl %ebp    popl %esi    popl %edi    popl %edx    popl %ecx    popl %ebx    popl %eax    iret           #返回被中断程序

以上内容是linux/kernel/asm.s中实现中断调用最核心的代码
完整的中断调用程序如下

_divide_error:    pushl $_do_divide_error    jmp  no_error_code

_divide_error是代表在内核中的一个地址,在汇编中代表定义了标号,在c语言中代表void divide_error(void);函数
然后我们再来看中断的初始化操作linux/kernel/traps.c中的trap_init(void)函数的实现

void trap_init(void){    int i;    set_trap_gate(0,&divide_error);    ...}

set_trap_gate(0,&divide_error)就是将中断向量表中第1项的中断处理程序设置为divide_error,也就是asm.s中定义的_divide_error标号,而在_divide_error中push $_do_divide_error变量是一个C函数,这个函数在traps.c中定义

void do_divide_error(long esp, long error_code){    die("divide error",esp,error_code);}

以上设置中断处理函数要特别注意中断向量号,上面为0,是什么意思?
这些就是Intel保留的中断向量号的定义,也就是从CPU发出的俗成约定,这个地方我理解了很久:!

中断号 名称 类型 信号 说明 0 Devide error 故障 SIGFPE 当进行除以零的操作时产生 1 Debug 陷阱故障 SIGTRAP 2 nmi 硬件 由不可屏蔽中断NMI产生 3 Breakpoint 陷阱 SIGTRAP 由断点指令int3产生,与debug处理相同 4 Overflow 陷阱 SIGSEGV eflags的溢出标志OF 引起 5 Bounds check 故障 SIGSEGV 寻址到有效地址意外时引起 6 Invalid Opcode 故障 SIGILL CPU执行时发现一个无效的指令操作码 7 Device not available 故障 SIGSEGV 8 Double fault 异常中止 SIGSEGV 双故障出错 9 Coprocessor segment overrun 异常中止 SIGFPE 协处理器段超出 10 Invalid TSS 故障 SIGSEGV CPU切换时发觉TSS无效 11 Segment not present 故障 SIGBUS 描述符所指的段不存在 12 Stack segment 故障 SIGBUS 堆栈段不存在或者寻址越出堆栈段 13 General protection 故障 SIGSEGV 没有符合80386保护机制的操作引起 14 Page fault 故障 SIGSEGV 页不再内存 15 Reserved 16 Coprocessor error 故障 SIGPE 协处理器发出的出错信号引起

以上内容就是大部分硬件故障的中断处理函数的处理过程,剩下的部分就是对8259A中断控制器的中断响应处理和系统调用(俗称软中断)

2. 8259A中断处理

理解8259A的中断处理过程,从理解一段代码开始:

    mov al,#0x11        #(ICW1设置)中断请求边沿触发多片8259级联并最需发送ICW4    out #0x20,al        ! send it to 8259A-1    .word   0x00eb,0x00eb       #0x00eb跳转到下一句的机器码    out #0xA0,al        ! and to 8259A-2    .word   0x00eb,0x00eb    mov al,#0x20        #(ICW2设置)主片中断号范围从0x20开始    out #0x21,al    .word   0x00eb,0x00eb    mov al,#0x28        #从片中断号范围从0x28开始    out #0xA1,al    .word   0x00eb,0x00eb    mov al,#0x04        #(ICW3设置)设置主芯片    out #0x21,al    .word   0x00eb,0x00eb    mov al,#0x02        #设置从芯片    out #0xA1,al    .word   0x00eb,0x00eb    mov al,#0x01        #(ICW4设置):普通EOI,非缓冲切需发送指令来复位的模式    out #0x21,al    .word   0x00eb,0x00eb    out #0xA1,al    #8259A中断控制器初始化结束    .word   0x00eb,0x00eb    mov al,#0xFF        #屏蔽所有中断请求    out #0x21,al    .word   0x00eb,0x00eb    out #0xA1,al

读取以上代码有困难的话,需要复习一下8259A中断控制器的相关知识,这是微机接口原理的主要内容,不过阅读上面代码的重点是告诉我们8259的中断向量是从0x20开始的,要记住这一点,不然时钟中断,硬盘中断等的中断向量号是怎么来的,你就不从知晓,可以参考一下列表:
这里写图片描述

现在来看具体的中断处理向量的设置,它们分散在不同的地方

1. 时钟中断向量设置

timer_interrupt这就是操作系统的心跳函数
linux/kernel/sched.c

void sched_init(void){    ...    set_intr_gate(0x20,&timer_interrupt);    ...}

2. 硬盘中断向量设置

linux/kernel/blk_drv/hd.c

void hd_init(void){    ...    set_intr_gate(0x2E,&hd_interrupt);    ...}

3. 键盘中断向量设置

linux/kernel/chr_drv/console.c

void con_init(void){    ...    set_trap_gate(0x21,&keyboard_interrupt);    ...}

想象一下,如果没有中断,那操作系统不是会一直要去查询各种设备的状态而忙死么

3. 系统调用(软中断)

0 0
原创粉丝点击