系统异常处理
CPU如果调用系统异常处理程序
需要的数据结构
- IDT_Table: 中断向量表, 在中断向量表中的每一项都是一个中断描述符(中断门或者陷阱门), 一个中断描述符中的几位是段选择符
- GDT或者LDT
处理过程(没有特权变化的情况),在处理中断和异常的时候, 需要处理程序, 注意: 是处理程序的代码, 每一个处理程序都要有的执行现场的保存工作
- 中断向量号 -> IDT -> 中断描述符 -> 段选择子 -> GDT/LDT -> 段描述符 -> 基地址, 同时得知中断描述符保存的段内偏移量 -> 中断处理程序(在开头最先执行的就是程序现场保留的工作, 在entry.S中完成, 在entry.S文件中定义了每一个寄存器在当前栈顶的偏移量, 用来在保留现场的时候将对应的寄存器的值放到对应的偏移量中, 这样方便恢复现场(恢复现场的程序也在entry.S中, 该文件中有一个模块(函数), 是将所有可用寄存器中的数据都压入到栈中))
处理过程(有特权变化的情况)
- CPU将EFLAGS, CS, EIP, Error Code都压入栈
注意
- 中断和异常向量都在一张IDT表中, IDT表总共256项
- IDT表的前32项都是异常向量, 但是21-31是Intel保留的不能使用, 所以我们实际上使用的是20个异常
- 我们已经在head.S程序的前半部分手动初始化了一下IDT(是不完整的初始化), 在head.S程序的后半阶段我们setup IDT表示, 给这256个异常和中断向量的异常处理程序都设置为同一个目标代码地址, 都是显示一串字符串"Error! Unknown interrupt or fault at RIP"
- 初始化完毕IDT之后, 紧接着我们要初始化TSS
真正进入到内核程度之后的系统异常处理
- 在trap.c文件中对每一个异常都提供一个异常处理函数(trap, interrupt, system)
- 在trap.c文件中, 使用set_trap_gate, set_intr_gate, set_system_gate三个函数为20个异常项提供处理函数, 处理函数名为nmi, divide_error, page_fault等, 他们都是在汇编层面上编写的, 因为C语言不能对寄存器进行栈操作, 在这些汇编函数中, 会调用各自的do_nmi, do_divide_error等子函数, 这些才是真正处理异常的逻辑封装, 是在C语言层面编写的, 主要就是编写do程序, 而nmi, divide_error的存在就是为了程序的现场恢复与保留等额外的处理
- 在do函数中, 我们要显示的是异常信息, 如果发生异常时的error_code(只有内部中断才会有错误码, 所以int指令和外部中断是没有的), rip, rsp, 以及通过分析error_code的32位得出的详细错误信息
转载于:https://www.cnblogs.com/megachen/p/9787186.html