目的:系统异常那么多,难道每个异常都定义一个函数吗?这就要涉及函数的重用
c语言里面可以利用宏来定义函数
汇编同样可以用宏来定义函数
.macro
.macro exception_handler name num with_error_code //传入三个参数函数的name,编号num,错误码with_error_code
.text
.macro exception_handler name num with_error_code // 异常的名字和代码
.extern do_handler_\name
.global exception_handler_\name
exception_handler_\name:
// 保存所有寄存器
// 如果没有错误码,压入一个缺省值
// 这样堆栈就和有错误码的情形一样了
.if \with_error_code == 0
push $0 // $表示立即数
.endif
// 压入异常号
push $\num
pusha // 保存通用寄存器
// 保存段寄存器
push %ds
push %es
push %fs
push %gs
push %esp
call do_handler_\name
add $(1*4), %esp // 丢掉esp,没有用pop指令,这块代码1需要重用
// 恢复保存的寄存器
pop %gs
pop %fs
pop %es
pop %ds
popa
// 跳过压入的异常号和错误码,有无错误码被硬件自动压入,全部都拿出去
add $(2*4), %esp
iret //中断返回指令,所以不能用c语言去写
.endm
因为有些异常会有Error Code,系统会自动压入,但是有些异常没有Error Code,为了统一处理(后面add $(2*4), %esp,不然这里就要分情况了),没有Error Code的异常我们假设其Error Code为0;
\name就是缺省的参数
这个地方有一个疑难点:
对于那些带Error Code的异常,最后我们pop的时候(add $(2*4), %esp),为什么要pop两个单元,系统自动压入的,还要我们手动弹出吗?要手动弹出,intel编程手册就是这么写的
利用汇编宏函数
//start.S
exception_handler unknown, -1, 0 //传入的三个残数
exception_handler divider, 0, 0
最后反汇编生成的东西
exception_handler_unknown
在irq.h里面定义好那个函数
void exception_handler_unknown (void);
void exception_handler_divider (void);
有个很奇葩的事,就是在宏汇编代码里面,无法调试
所以
到了除0这句代码,直接跳转到了
对比这句代码eip的值
除0代码的逻辑地址,对应上了
这是直接给出结论了,中间有安装idt表的过程(为什么0能对应上除0异常都是我们手动安排好的)
void irq_init(void) {
for (uint32_t i = 0; i < IDT_TABLE_NR; i++) {
gate_desc_set(idt_table + i, KERNEL_SELECTOR_CS, (uint32_t) exception_handler_unknown,
GATE_P_PRESENT | GATE_DPL0 | GATE_TYPE_IDT);
}
// 设置异常处理接口
irq_install(IRQ0_DE, (irq_handler_t)exception_handler_divider);
lidt((uint32_t)idt_table, sizeof(idt_table));
}
int irq_install(int irq_num, irq_handler_t handler) {
if (irq_num >= IDT_TABLE_NR) {
return -1;
}
gate_desc_set(idt_table + irq_num, KERNEL_SELECTOR_CS, (uint32_t) handler,
GATE_P_PRESENT | GATE_DPL0 | GATE_TYPE_IDT);
return 0;
}
解释irq_handler_t类型
//irq.h
typedef void(*irq_handler_t)(void);
是用来定义函数指针类型的
参数为void,返回值为void
pos:学操作系统底层要强压下来,不能学一天玩一天