x86-4-中断描述符
7.添加中断描述符表
中断执行的流程:
- 当某个中断发生时,典型的处理方式就是CPU会 打断当前的任务,保留当前的执行现场
- 再转移到该中断事先安排好的中断处理函数去执行。
- 在中断处理函数执行结束之后再恢复中断之前的执行现场,去执行之前的任务。
中断描述符表(IDT,interrupt descriptor table) 与gdt 类似的 也有一个中断描述符表寄存器 记录这个表的起始地址
include/idt.h 定义
1 |
|
按照Intel的规定,0~19号中断属于CPU所有62,而且第 20-31号中断也被Intel保留,所以从32~255号才属于用户自定义中断。虽说是”用户自定 义”,其实在x86上有些中断按照习惯还是给予了固定的设备。比如32号是timer中断,33 号是键盘中断等等。
虽然cpu在中断产生的时候自动保存了部分的执行现场,但是依然有很多寄存器需要我们自己保存和恢复
include/idt.h
1 | typedef |
把原本的中断处理函数逻辑上拆解为三部分,第一部分是一致 的现场保护操作;第二部分是每个中断特有的处理逻辑;第三部分又是一致的现场恢复。
实际上拆分成了四段。
idt/idt_s.s
1 | ; -------------------------------------------------- |
idt_s.s
共有的现场保护操作
1 | ; 共有的现场保护的部分 |
idt/idt.c
1 |
|
bzero((uint8_t *)&interrupt_handlers, sizeof(interrupt_handler_t) * 256);
确保在初始化这些结构之前,它们不包含任何无意义的数据。
修改入口函数
init/entry.c
1 |
|
8.完成中断请求和定时器中断
8259A 的PIC 架构:主从架构
8.1 8259A PIC的初始化
idt/idt.c
在init_idt中最前面加入:
1 | // 重新映射 IRQ 表 |
outb(0x20, 0x11);
- 这里的 0x11 是初始化命令,其中最高四位(0001)告诉 PIC 进行级联模式初始化,最低四位(0001)则指示 PIC 等待额外的初始化字节。
outb(0x21, 0x20);
- 设置主 PIC 的偏移(0x20,即 32)和从 PIC 的偏移(0x28,即 40)。这意味着 IRQ0 将映射到中断向量 0x20,IRQ8(从 PIC 的第一个中断)映射到中断向量 0x28。
outb(0x21, 0x04);
设置主片 IR2 引脚连接从片
- 0x21 :主PIC 的中断屏蔽寄存器的端口地址在初始化序列中,对于 8259A PIC,
0x21
在发送初始化命令(ICW1)后也用于写入初始化命令字 2(ICW2)和初始化命令字 3(ICW3)。 - 0x04:00000100 IRQ2.在设置 ICW3 时,这个值的二进制中的每一位表示一个 IRQ 输入是否连接到从 PIC。
- 0x02:这个值在二进制表示中为
00000010
,其中,位设置表示从 PIC 连接到主 PIC 的 IRQ2 端口(对于主 PIC 端的配置是0x04
,即第二个 IRQ,从0开始计数)。
include/idt.h 对IQR处理函数的添加
1 | // IRQ 处理函数 |
ISR (Interrupt Service Routine) 中断服务例程, 主要处理 内核级的中断,除0 溢出等 立即报警打断原有的进程
IRQ (Interrupt Request) 中断请求,主要用于处理外设的一些中断 键盘 鼠标等 请求嘛 允许后才会产生中断
idt/idt_s.s 添加相应的处理过程
1 | ; 构造中断请求的宏 |
这个宏定义用于构建中断处理程序的框架。每个中断处理程序都有一定的共性,例如禁用中断(cli
,Clear Interrupt Flag)、保存状态、调用共通处理程序等。
%macro IRQ 2
:定义一个带有两个参数的宏,**%1
是中断号,%2
是中断向量。**cli
:清除中断标志,防止中断处理程序被其他中断打断。push 0
:对于某些中断(如除错、溢出等),CPU 会自动压入错误代码;对于不会自动压入错误代码的中断(如外部中断),为了统一处理流程,在这里手动压入一个占位的错误代码。push %2
:压入中断向量编号,供中断处理程序使用。jmp irq_common_stub
:跳转到通用的中断处理代码。
idt/idt.c 构造irq相关的 描述符和 具体的irq函数
1 | idt_set_gate(32, (uint32_t)irq0, 0x08, 0x8E); |
ISR 和 iRQ 的处理过程:
- ISR的处理过程是 (isr0 - isr31) -> isr_common_stub(相同的处理部分) -> isr_handler -> 具体的ISR处理函数。
- IRQ的处理过程是 (irq0 - irq15) -> irq_common_stub -> irq_hanlder -> 具体的IRQ处理函数。
8.2 时钟中断和产生处理
配置8253/8254 timer 芯片
drivers/timer.c
1 |
|
1 | void timer_callback(pt_regs *regs) |
回调函数
tick 静态成员变量 用来记录中断发生的次数
printk_color(rc_black, rc_red, "Tick: %d\n", tick++);
: 在屏幕上打印当前的tick数,并以红色字体显示。每调用一次这个函数,tick
的值就增加1。
include/timer.h
1 |
|
修改入口函数
init/entry.c
1 |
|
asm volatile("sti")
通常在系统末尾调用
- 在汇编语言中,
sti
指令的作用是设置中断标志(Set Interrupt Flag) - 在操作系统启动过程中,中断通常在执行初期阶段被禁用(通常使用
cli
指令,即 Clear Interrupt Flag),以避免在系统未完全配置好时响应中断,这可能导致不稳定或错误。 - 在系统的关键组件如GDT、IDT和定时器等被初始化和配置好之后,使用
sti
指令允许中断,是为了启动正常的中断处理,这对于系统的进一步功能如多任务处理、响应外部设备请求等是必要的。