中断处理 - 练习题
练习 1:理解中断向量
回答以下问题:
- x86 CPU预定义了多少个异常向量?
- 为什么PIC的IRQ需要重映射?原始映射会导致什么问题?
- 向量14(页错误)的错误码包含哪些信息?
练习 2:注册自定义中断处理器
在 kernel.c 中注册一个断点(INT 3)处理器:
void breakpoint_handler(registers_t *regs) {
screen_set_color(VGA_COLOR_YELLOW, VGA_COLOR_BLACK);
screen_printf("[BREAKPOINT] at EIP: 0x%x\n", regs->eip);
screen_set_color(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK);
}
// 在kernel_main中注册并测试
register_interrupt_handler(3, breakpoint_handler);
__asm__ volatile("int $3"); // 触发断点验证处理器被正确调用并打印了EIP。
练习 3:实现定时器中断
时钟中断(IRQ0)是向量32。实现一个简单的计时器:
static uint32_t tick = 0;
void timer_handler(registers_t *regs) {
tick++;
if (tick % 100 == 0) { // 约每秒一次
screen_printf("[TIMER] %d seconds\n", tick / 100);
}
}
// 注册
register_interrupt_handler(32, timer_handler);练习 4:PIC中断掩码
学习使用中断掩码控制IRQ:
// 禁用某个IRQ
void irq_set_mask(uint8_t irq) {
uint16_t port;
if (irq < 8) {
port = 0x21;
} else {
port = 0xA1;
irq -= 8;
}
uint8_t value = inb(port) | (1 << irq);
outb(port, value);
}
// 启用某个IRQ
void irq_clear_mask(uint8_t irq) {
// 提示:与上面相反,使用 & ~(1 << irq)
}实现 irq_clear_mask,并测试只启用键盘中断(IRQ1)。
练习 5:异常信息增强
修改 isr_handler 函数,在异常发生时打印更多寄存器信息:
要求显示:
- 所有通用寄存器(EAX, EBX, ECX, EDX, ESI, EDI, EBP, ESP)
- 段寄存器(DS, CS)
- 控制寄存器(EFLAGS)
- 错误码
格式参考:
=== REGISTER DUMP ===
EAX=0x00000000 EBX=0x00000000 ECX=0x00000000 EDX=0x00000000
ESI=0x00000000 EDI=0x00000000 EBP=0x00000000 ESP=0x00000000
EIP=0x00001234 CS=0x0008 DS=0x0010 EFLAGS=0x00000202
INT=0x0E ERR=0x00000002
思考题
-
为什么中断处理程序中需要切换到内核数据段(0x10)? 如果不切换会发生什么?
-
IRET和RET有什么区别? 为什么中断处理程序不能用RET返回?
-
如果在中断处理程序中再次发生中断(中断嵌套),会发生什么? 32位中断门(0x8E)是如何处理这个问题的?
-
假设系统频繁发生伪中断(IRQ7/IRQ15),你应该如何处理? 提示:查阅ISR(In-Service Register)。