zoukankan      html  css  js  c++  java
  • Linux 0.01 中断部分简要及注释

    Linux 0.01 中断部分简要及注释
    Willweb 于4.10
    中断是linux 系统中的一个重要并且复杂的组成部分,它提供给外设一种运行cpu 的方
    式,一个完善的支持外设扩充的系统中,由于有着多种多样的外部设备,这些设备通过中断
    方式来平衡速度和获得资源。
    中断可以分为cpu 的内部中断和外设的外部中断两种,cpu 的内部中断又可以叫做异常,
    异常的主要作用是报告一些程序运行过程中的错误和处理缺页中断(page_fault),这些异
    常都是通过set_trap_gate 来设置的,intel 的cpu 为trap 保留了32 个,具体在idt 中的
    号是从00 到20。外部设备中断是通过set_intr_gate 来设置的,它从21 开始到ff。另外
    还有一种软件中断,也就是前一次讲到的系统调用,它是在用户端可以调用的。
    Linux2。4 的中断处理比较复杂,因为涉及的设备比较多,同时顾及到许多处理的问题,
    但是0。01 由于比较简单,没有处理很复杂的操作,它里面主要做了几个部分的工作:
    1. Trap.s 将各个cpu 的trap 信息填入到idt 中
    2. Asm.s 处理当出现trap 的时候后续的一系列处理
    3. 另外,0。01 中的处理的外部中断有time(0x20),串口(0x23 和0x24),硬盘(0x2e),
    键盘(0x21),具体的处理函数在rs_io.s,hd.c 和keyboard.s 中
    linux 下都是采用两片8259 作为接收外部的中断控制器,其初始化是在boot.s 中初始
    化的,具体的连接是8259 的从片接主片的第二个中断口,其他的中断线就可以通过其他设
    备共享,到外设产生一个中断之后,8259 自己将irq 号转换为中断向量(一般是加上20),
    然后发给cpu 并进行等待,当cpu 应该之后再清intr 线,cpu 接收到中断之后在内核堆栈
    中保留irq 值和寄存器值,同时发送一个pic 应答并执行isr 中断服务程序。
    如果多个设备共享一个中断,那么每当一个设备产生一个中断的时候cpu 会执行所有的
    isr 中断服务程序。具体的一个完整的中断产生和处理流程是:
    产生中断――>cpu 应答――>查找idt 中的对应向量――>在gdt 中查找idt 项的代码
    段――>对比当前的cpl 和描述符的dpl 看是否产生越级保护――>检查是否发生特权级的
    变化,如果是就保存ss 和esp,否则不保存――>保存eflags、cs、eip 和错误码――>将
    idt 对应描述符地址装入cs 和eip 中以便执行――>执行irq_interrupt――>执行
    do_irq――>循环执行isr――>返回
    /KERNEL/TRAPS.C 简要注释
    /*
    * 'Traps.c' handles hardware traps and faults after we have saved some
    * state in 'asm.s'. Currently mostly a debugging-aid, will be extended
    * to mainly kill the offending process (probably by giving it a signal,
    * but possibly by killing it outright if necessary).
    */
    #include
    #include
    #include
    #include
    #include
    #include
    //得到段寄存器中addr 地址的数据,返回一个字节
    #define get_seg_byte(seg,addr) ({ \
    register char __res; \
    __asm__("push %%fs;mov %%ax,%%fs;movb %%fs:%2,%%al;pop %%fs" \
    :"=a" (__res):"0" (seg),"m" (*(addr))); \
    __res;})
    //返回四个字节的addr 地址的段寄存器内容
    #define get_seg_long(seg,addr) ({ \
    register unsigned long __res; \
    __asm__("push %%fs;mov %%ax,%%fs;movl %%fs:%2,%%eax;pop %%fs" \
    :"=a" (__res):"0" (seg),"m" (*(addr))); \
    __res;})
    //取得fs 段寄存器的内容
    #define _fs() ({ \
    register unsigned short __res; \
    __asm__("mov %%fs,%%ax":"=a" (__res); \
    __res;})
    //当出现程序内部错误的时候(例如除0 等异常),打印出cpu 中各个寄存器的值然后退出程

    static void die(char * str,long esp_ptr,long nr)
    {
    long * esp = (long *) esp_ptr;
    int i;
    //显示出错信息并打印出每个寄存器的值
    printk("%s: %04x\n\r",str,nr&0xffff);
    printk("EIP:\t%04x:%p\nEFLAGS:\t%p\nESP:\t%04x:%p\n",
    esp[1],esp[0],esp[2],esp[4],esp[3]);
    printk("fs: %04x\n",_fs());
    printk("base: %p, limit: %p\n",get_base(current->ldt[1]),get_limit(0x17));
    if (esp[4] == 0x17) {
    printk("Stack: ");
    for (i=0;i<4;i++)
    printk("%p ",get_seg_long(0x17,i+(long *)esp[3]));
    printk("\n");
    }
    str(i);
    printk("Pid: %d, process nr: %d\n\r",current->pid,0xffff & i);
    for(i=0;i<10;i++)
    printk("%02x ",0xff & get_seg_byte(esp[1],(i+(char *)esp[0])));
    printk("\n\r");
    //退出程序
    do_exit(11); /* play segment exception */
    }
    //异常处理程序
    void do_double_fault(long esp, long error_code)
    {
    die("double fault",esp,error_code);
    }
    void do_general_protection(long esp, long error_code)
    {
    die("general protection",esp,error_code);
    }
    void do_divide_error(long esp, long error_code)
    {
    die("divide error",esp,error_code);
    }
    //do_int3 处理断点的trap,具体是将所有的寄存器值打印出来
    void do_int3(long * esp, long error_code,
    long fs,long es,long ds,
    long ebp,long esi,long edi,
    long edx,long ecx,long ebx,long eax)
    {
    int tr;
    __asm__("str %%ax":"=a" (tr):"0" (0));
    printk("eax\t\tebx\t\tecx\t\tedx\n\r%8x\t%8x\t%8x\t%8x\n\r",
    eax,ebx,ecx,edx);
    printk("esi\t\tedi\t\tebp\t\tesp\n\r%8x\t%8x\t%8x\t%8x\n\r",
    esi,edi,ebp,(long) esp);
    printk("\n\rds\tes\tfs\ttr\n\r%4x\t%4x\t%4x\t%4x\n\r",
    ds,es,fs,tr);
    printk("EIP: %8x CS: %4x EFLAGS: %8x\n\r",esp[0],esp[1],esp[2]);
    }
    //以下的trap 操作转化为die 函数操作
    void do_nmi(long esp, long error_code)
    {
    die("nmi",esp,error_code);
    }
    void do_debug(long esp, long error_code)
    {
    die("debug",esp,error_code);
    }
    void do_overflow(long esp, long error_code)
    {
    die("overflow",esp,error_code);
    }
    void do_bounds(long esp, long error_code)
    {
    die("bounds",esp,error_code);
    }
    void do_invalid_op(long esp, long error_code)
    {
    die("invalid operand",esp,error_code);
    }
    void do_device_not_available(long esp, long error_code)
    {
    die("device not available",esp,error_code);
    }
    void do_coprocessor_segment_overrun(long esp, long error_code)
    {
    die("coprocessor segment overrun",esp,error_code);
    }
    void do_invalid_TSS(long esp,long error_code)
    {
    die("invalid TSS",esp,error_code);
    }
    void do_segment_not_present(long esp,long error_code)
    {
    die("segment not present",esp,error_code);
    }
    void do_stack_segment(long esp,long error_code)
    {
    die("stack segment",esp,error_code);
    }
    void do_coprocessor_error(long esp, long error_code)
    {
    die("coprocessor error",esp,error_code);
    }
    void do_reserved(long esp, long error_code)
    {
    die("reserved (15,17-31) error",esp,error_code);
    }
    //初始化trap 信息,将前面的32 个idt 填入异常处理函数的地址
    void trap_init(void)
    {
    int i;
    //系统门和陷阱门的类型都是1111(15),而中断门类型是1110(14),但是中断门和陷阱门的
    dpl 都是0,而系统门的dpl 是3
    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);
    for (i=17;i<32;i++)
    set_trap_gate(i,&reserved);
    /* __asm__("movl $0x3ff000,%%eax\n\t"
    "movl %%eax,%%db0\n\t"
    "movl $0x000d0303,%%eax\n\t"
    "movl %%eax,%%db7"
    :::"ax");*/
    }
    /KERNEL/ASM.S 具体的异常处理程序
    /*
    * asm.s contains the low-level code for most hardware faults.
    * page_exception is handled by the mm, so that isn't here. This
    * file also handles (hopefully) fpu-exceptions due to TS-bit, as
    * the fpu must be properly saved/resored. This hasn't been tested.
    */
    .globl _divide_error,_debug,_nmi,_int3,_overflow,_bounds,_invalid_op
    .globl _device_not_available,_double_fault,_coprocessor_segment_overrun
    .globl _invalid_TSS,_segment_not_present,_stack_segment
    .globl _general_protection,_coprocessor_error,_reserved
    //这里的asm.s 的处理流程是在每个处理函数中先把处理函数的地址压栈,然后调用
    no_error_code 或error_code,在no_error_code 或error_code 中调用压入的地址再执行具
    体的函数
    _divide_error:
    pushl $_do_divide_error
    no_error_code:
    //eax 得到压入的函数地址
    xchgl %eax,(%esp)
    //保存各个寄存器变量
    pushl %ebx
    pushl %ecx
    pushl %edx
    pushl %edi
    pushl %esi
    pushl %ebp
    push %ds
    push %es
    //压入一个错误码,由于这里是no_error_code 那么错误码是0
    push %fs
    pushl $0 # "error code"
    //得到栈顶地址,以方便检查哪里出了错误
    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
    //每一个处理函数的流程都一样,先压入一个处理函数地址,然后在转跳到no_error_code
    执行
    _debug:
    pushl $_do_int3 # _do_debug
    jmp no_error_code
    _nmi:
    pushl $_do_nmi
    jmp no_error_code
    _int3:
    pushl $_do_int3
    jmp no_error_code
    _overflow:
    pushl $_do_overflow
    jmp no_error_code
    _bounds:
    pushl $_do_bounds
    jmp no_error_code
    _invalid_op:
    pushl $_do_invalid_op
    jmp no_error_code
    math_emulate:
    popl %eax //弹出eax,因为在调用math_emulate 的时候push 了eax
    pushl $_do_device_not_available
    jmp no_error_code
    _device_not_available:
    pushl %eax
    movl %cr0,%eax
    //测试是否需要模拟协处理器
    bt $2,%eax # EM (math emulation bit)
    jc math_emulate
    //如果不模拟的话,就用协处理器
    clts # clear TS so that we can use math
    movl _current,%eax
    cmpl _last_task_used_math,%eax
    je 1f # shouldn't happen really ...
    pushl %ecx
    pushl %edx
    push %ds
    movl $0x10,%eax
    mov %ax,%ds
    call _math_state_restore
    pop %ds
    popl %edx
    popl %ecx
    1: popl %eax
    iret
    _coprocessor_segment_overrun:
    pushl $_do_coprocessor_segment_overrun
    jmp no_error_code
    _reserved:
    pushl $_do_reserved
    jmp no_error_code
    _coprocessor_error:
    pushl $_do_coprocessor_error
    jmp no_error_code
    //如果有错误码的话,那么在执行对应的函数之前自动会把错误码压入的
    _double_fault:
    pushl $_do_double_fault
    error_code:
    xchgl %eax,4(%esp) # error code <-> %eax
    xchgl %ebx,(%esp) # &function <-> %ebx
    //保存寄存器变量
    pushl %ecx
    pushl %edx
    pushl %edi
    pushl %esi
    pushl %ebp
    push %ds
    push %es
    push %fs
    //这里压入的是错误码而不是0 了
    pushl %eax # error code
    lea 44(%esp),%eax # offset
    pushl %eax
    movl $0x10,%eax
    mov %ax,%ds
    mov %ax,%es
    mov %ax,%fs
    //调用处理函数
    call *%ebx
    addl $8,%esp
    pop %fs
    pop %es
    pop %ds
    popl %ebp
    popl %esi
    popl %edi
    popl %edx
    popl %ecx
    popl %ebx
    popl %eax
    iret
    _invalid_TSS:
    pushl $_do_invalid_TSS
    jmp error_code
    _segment_not_present:
    pushl $_do_segment_not_present
    jmp error_code
    _stack_segment:
    pushl $_do_stack_segment
    jmp error_code
    _general_protection:
    pushl $_do_general_protection
    jmp error_code
  • 相关阅读:
    phpexcel读取excel文件
    laravel的表单验证(下面有些信息未验证,转的)
    Laravel框架学习(Response)
    laravel文件存储Storage
    Laravel 5 教程
    js中substring和substr的用法
    jquery 规范
    总结oninput、onchange与onpropertychange事件的用法和区别
    jquery 选择器的总结
    jquery选择器中的find和空格,children和>的区别、及父节点兄弟节点,还有判断是否存在的写法
  • 原文地址:https://www.cnblogs.com/huqingyu/p/114861.html
Copyright © 2011-2022 走看看