这一节主要是装载中断。
所需要的文件在Github:https://github.com/yongkangluo/Ubuntu20.04OS/tree/main/Files/Lec4-Interrupts
1. 中断相关知识
中断: 分为两种:非屏蔽中断(Non-Maskable Interrupts); 可屏蔽中断(Maskable Interrupts)。
如何让CPU忽略中断,使用的就是 EFLAGS(Program status and control)寄存器。
IF(bit 9) [Interrupt enable flag] 该标志用于控制处理器对可屏蔽中断请求(maskable interrupt requests)的响应。置1以响应可屏蔽中断,反之则禁止可屏蔽中断。
Interrupt Descriptor Table (IDT) 中断向量描述表
为了让系统找到 IDT需要一个 IDTR 即 IDT 寄存器。
其与 GDT 非常相似。
可以使用基地址访问IDT。
下面就是 IDT 和 Interrupt Vector 的关系:IDT 中的中断向量,包括了偏移地址和段选择器,就可以根据利用这些去GDT中查询到相应的中断处理程序(ISP)。
2. 中断向量表代码实现
// idt.c
#define IDT_ENTRY 32
uint64_t _idt[IDT_ENTRY];
uint16_t _idt_limit = sizeof(_idt) - 1;
void _set_idt_entry(uint32_t vector, uint16_t seg_selector, void (*isr)(), uint8_t dpl){
uintptr_t offset = (uintptr_t) isr;
_idt[vector] = ((offset & 0xffff0000) | IDT_ATTR(dpl));
_idt[vector] <<= 32;
_idt[vector] |= seg_selector << 16 | (offset & 0x0000ffff);
}
void _init_idt(){
_set_idt_entry(FAULT_DIVISION_ERROR, 0x08, _asm_isr0, 0);
// 其中 _asm_isr0 就是中断程序的入口
}
之后将 idt 加载进 lidt 寄存器;
//boot.S
movl $_idt, 2(%esp)
movw _idt_limit, %ax
movw %ax, (%esp)
lidt (%esp)
3. ISR(Interrupt Service Routine) 实现
Interrupt handler 设计:
存在一个问题,无法利用C语言设计一个形如这种的 interrupt handler:
/* How NOT to write an interrupt handler */
void interrupt_handler(void)
{
asm("pushad"); /* Save registers. */
/* do something */
asm("popad"); /* Restore registers. */
asm("iret"); /* This will triple-fault! */
}
编译器不明白发生了什么。它不明白,寄存器和堆栈必须保留在 asm 语句之间; 优化器可能会破坏函数。此外,编译器在函数之前和之后添加堆栈处理代码,这与 iret 结果类似于下面的汇编代码:
push %ebp
mov %esp,%ebp
sub $<size of local variables>,%esp
pushad
# C code comes here
popad
iret
# 'leave' if you use local variables, 'pop %ebp' otherwise.
leave
ret
解决方法:使用汇编语言写!
新建一个.S 文件
//interrupts.S
// 宏定义
.macro isr_template vector, no_error_code = 1
.global _asm_isr\vector
.type _asm_isr\vector, @function
_asm_isr\vector:
.if \no_error_code
pushl $0x0
.endif
pushl $\vector
jmp interrupt_wrapper
.endm
.section .text
isr_template 0
interrupt_wrapper:
movl %esp, %eax
andl $0xfffffff0, %esp
subl $16, %esp
movl %eax, (%esp)
call interrupt_handler
pop %eax
movl %eax, %esp
addl $8, %esp
iret
关于ISR的参数
方法主要跟着B站Up主做的,B站视频链接在:https://www.bilibili.com/video/BV1jL4y1s7X6/?spm_id_from=333.788&vd_source=72ce864f895f9fbf22b81450817f2875