lab1 中对中断的处理实现

(1) 外设基本初始化设置

Lab1 实现了中断初始化和对键盘、串口、时钟外设进行中断处理。串口的初始化函数 serial_init(位于/kern/driver/console.c)中涉及中断初始化工作的很简单:

......
// 使能串口1接收字符后产生中断
    outb(COM1 + COM_IER, COM_IER_RDI);
......
// 通过中断控制器使能串口1中断
pic_enable(IRQ_COM1);

键盘的初始化函数 kbd_init(位于 kern/driver/console.c 中)完成了对键盘的中断初始化工作,具体操作更加简单:

......
// 通过中断控制器使能键盘输入中断
pic_enable(IRQ_KBD);

时钟是一种有着特殊作用的外设,其作用并不仅仅是计时。在后续章节中将讲到,正是由于有了规律的时钟中断,才使得无论当前 CPU 运行在哪里,操作系统都可以在预先确定的时间点上获得 CPU 控制权。这样当一个应用程序运行了一定时间后,操作系统会通过时钟中断获得 CPU 控制权,并可把 CPU 资源让给更需要 CPU 的其他应用程序。时钟的初始化函数 clock_init(位于 kern/driver/clock.c 中)完成了对时钟控制器 8253 的初始化:

    ......
//设置时钟每秒中断100次
    outb(IO_TIMER1, TIMER_DIV(100) % 256);
    outb(IO_TIMER1, TIMER_DIV(100) / 256);
// 通过中断控制器使能时钟中断
    pic_enable(IRQ_TIMER);

(2) 中断初始化设置

操作系统如果要正确处理各种不同的中断事件,就需要安排应该由哪个中断服务例程负责处理特定的中断事件。系统将所有的中断事件统一进行了编号(0 ~ 255),这个编号称为中断向量。以 ucore 为例,操作系统内核启动以后,会通过 idt_init 函数初始化 idt 表 (参见 trap.c),而其中 vectors 中存储了中断处理程序的入口地址。vectors 定义在 vector.S 文件中,通过一个工具程序 vector.c 生成。其中仅有 System call 中断的权限为用户权限 (DPL_USER),即仅能够使用 int 0x80 指令。此外还有对 tickslock 的初始化,该锁用于处理时钟中断。

vector.S 文件通过 vectors.c 自动生成,其中定义了每个中断的入口程序和入口地址 (保存在 vectors 数组中)。其中,中断可以分成两类:一类是压入错误编码的 (error code),另一类不压入错误编码。对于第二类, vector.S 自动压入一个 0。此外,还会压入相应中断的中断号。在压入两个必要的参数之后,中断处理函数跳转到统一的入口 alltraps 处。

(3) 中断的处理过程

trap 函数(定义在 trap.c 中)是对中断进行处理的过程,所有的中断在经过中断入口函数__alltraps 预处理后 (定义在 trapasm.S 中) ,都会跳转到这里。在处理过程中,根据不同的中断类型,进行相应的处理。在相应的处理过程结束以后,trap 将会返回,被中断的程序会继续运行。整个中断处理流程大致如下:

trapasm.Strap.c
1)产生中断后,CPU 跳转到相应的中断处理入口 (vectors),并在桟中压入相应的 error_code(是否存在与异常号相关) 以及 trap_no,然后跳转到 alltraps 函数入口:
注意:此处的跳转是 jmp 过程
(high)...
产生中断时的 eip →eip
error_code
esp →trap_no
(low)...

在栈中保存当前被打断程序的 trapframe 结构(参见过程trapasm.S)。设置 kernel (内核) 的数据段寄存器,最后压入 esp,作为 trap 函数参数(struct trapframe * tf) 并跳转到中断处理函数 trap 处:
Struct trapframe
{
uint edi;
uint esi;
uint ebp;

ushort es;
ushort padding1;
ushort ds;
ushort padding2;
uint trapno;
uint err;
uint eip;
...
}
观察 trapframe 结构与中断产生过程的压桟顺序。
需要明确 pushal 指令都保存了哪些寄存器,按照什么顺序?



← trap_no
← trap_error
← 产生中断处的 eip

注意:此时的跳转是 call 调用,会压入返回地址 eip,注意区分此处eip与trapframe中eip:
trapframe的结构为:
进入 trap 函数,对中断进行相应的处理:
2)详细的中断分类以及处理流程如下:
根据中断号对不同的中断进行处理。其中,若中断号是IRQ_OFFSET + IRQ_TIMER 为时钟中断,则把ticks 将增加一。
若中断号是IRQ_OFFSET + IRQ_COM1 为串口中断,则显示收到的字符。
若中断号是IRQ_OFFSET + IRQ_KBD 为键盘中断,则显示收到的字符。
若为其他中断且产生在内核状态,则挂起系统;
3)结束 trap 函数的执行后,通过 ret 指令返回到 alltraps 执行过程。
从栈中恢复所有寄存器的值。
调整 esp 的值:跳过栈中的 trap_no 与 error_code,使esp指向中断返回 eip,通过 iret 调用恢复 cs、eflag以及 eip,继续执行。

图 13 ucore 中断处理流程

至此,对整个 lab1 中的主要部分的背景知识和实现进行了阐述。请大家能够根据前面的练习要求完成所有的练习。

results matching ""

    No results matching ""