在上一篇文章中我们完成了对GDT,IDT,TSS以及PIC8259A的初始化以及加载工作,现在我们需要为操作系添加一些异常处理功能,并且使用8259A编写一个简易的键盘驱动
注册异常处理函数
在实现IDT
的过程中我们提到了x86-interrut调用约定
(详情请查看使用Rust开发操作系统(中断描述符表–IDT)关于调用约定章节)注册处理函数的诀窍是 首先编写一个 extern x86-interrupt
中断处理函数,函数的参数是根据你要处理异常的类别去传递的,主要分为2种,带错误码的和不带错误码的,然后将编写的函数注册到对应的异常中去
例如:
// 注:库及结构已经更改过,请以github代码为准(https://github.com/VenmoTools/OperatingSystem)
use system::ia_32e::descriptor::{InterruptDescriptorTable, InterruptStackFrame};
use system::bits::PageFaultErrorCode;
// 不带错误码
extern "x86-interrupt" fn example_interrupt(_stackframe: &mut InterruptStackFrame) {
}
// 带错误码(错误码需要解析)
extern "x86-interrupt" fn exmple_with_err_code(stackframe: &mut InterruptStackFrame, err: u64) {
}
// 页异常需要单独的错误码
extern "x86-interrupt" fn page_fault(stackframe: &mut InterruptStackFrame, code: PageFaultErrorCode) {
}
在使用之前一定在kernel/src/main.rs
或者kernel/src/lib.rs
文件的头部添加一下内容
#![feature(abi_x86_interrupt)]
注册异常处理函数例子
extern "x86-interrupt" fn page_fault(stackframe: &mut InterruptStackFrame, code: PageFaultErrorCode) {
......
}
....
let mut idt = InterruptDescriptorTable::new();
idt.page_fault.set_handler_fn(page_fault);
....
需要注意的是,中断和异常都需要用到IDT,注册中断处理函数和异常处理函数的方式有很大区别在下文中我们详细阐述注册中断处理函数
定义异常处理函数
好了,掌握异常处理函数的注册技巧后我们开始注册20个异常处理函数(木有办法。。就是这么多),我们暂时只打印异常信息不做具体处理(为了完整性,列出了所有异常)
// in kernel/src/descriptor/idt.rs
// 调试中断
extern "x86-interrupt" fn breakpoint(stackframe: &mut InterruptStackFrame) {
println!("EXCEPTION:BREAKPOINT\n{:#?}", stackframe);
}
// 缺页错误
extern "x86-interrupt" fn page_fault(stackframe: &mut InterruptStackFrame, code: PageFaultErrorCode) {
println!("EXCEPTION:PageFault");
println!("Error Code:{:?}", code);
println!("{:#?}", stackframe);
loop_hlt();
}
// 除零错误
extern "x86-interrupt" fn divide_by_zero(stackframe: &mut InterruptStackFrame) {
println!("EXCEPTION:divide_by_zero\