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

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发出的俗成约定,这个地方我理解了很久:!

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

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值