Linux内核学习笔记:中断与异常
中断分为同步中断与异步中断。同步中断也叫异常是CPU执行特定的指令产生的事件,他打断CPU正常执行的指令而执行设定好的指令。异步中断也叫中断是由CPU外部中断信号产生的,每个CPU都有一个或多个中断引脚,当引脚上出现中断中断信号的时候,CPU就会停止执行当前的指令而去执行特定的代码。在linux中,中断处理至关重要,它影响着整个系统的性能。中断程序运行时,当前进程Current宏无效,所以中断程序是一个单独的内核控制路径,不能够进行进程切换。
因为中断的特殊性,所以中断处理设计就必须考虑以下几点:
(1)中断处理时间尽可能短,这个可以通过一定的方法使得中断分阶段进行。
(2)中断嵌套的允许,这样极大缩短了中断延迟
(3)关中断的尽可能时间短,次数少。
x86系统中使用中断描述符来描述各种中断,中断描述符表是个系统表,首地址存放在idtr寄存器中。中断描述符中装有段选择符以及段内偏移量,这个来确定中断处理程序的地址。
一. 内核初始化中断描述符表
中断描述符表必须在开中断时初始化,初始化这个表,linux有两个阶段。第一个阶段是初步初始化,在中断描述符表中,插入相同的表项,中断描述符表有256相,这个表项指向的中断处理程序,只是保存一些寄存器,然后打印“Unknown interrupt”,然后就退出。第二阶段,linux用有意义的中断处理程序的地址填充表项。x86体系结构中256个中断向量分配如下:
0-19 非屏蔽中断与异常
20-31 保留
32-127 外部中断
128 系统调用异常
129-238 外部中断
......
二. 中断与异常的硬件处理
在中断发生时,CPU硬件首先要完成一定的工作,使得程序计数器能够正确跳转到中断处理程序中。x86体系结构的中断硬件处理如下:
(1) 通过读取中断控制器的IO端口,或得中断号,并且由此中断号在中断描述符表中找到相应的中断描述符。
(2)检查中断的权限。总体的原则是引起中断的程序的特权级别CPL(当前特权级)必须高于低于中断处理程序的特权。
(3)检查是否发生特权级的变化,也就是检查是在内核态发生的中断,还是在用户态发生的中断。如果在用户态发生的中断,内核发生中断CPU肯定切换到了内核态,所以特权级,肯定发生了变化。如果在内核态发生的中断,特权级就没有变。如果是由用户态切换到内核态那么,在ss和esp中还保留这用户态堆栈的地址,所以得用内核态的地址填充。
(4)装载cs,eip地址,值是在中断描述符表中获取的。
三. 同步中断:异常的内核处理
CPU产生的异常,内核都当作错误处理。但是有一个异常是linux利用CPU高效管理硬件的前提,那就是缺页异常,注意,同步中断当前进程有效。异常处理程序一般分为三部分:
(1) 在内核堆栈中保存大多数CPU寄存器的内容
这部分是用汇编程序编写的,首先将C中断处理函数的地址压入堆栈,然后保存8个C语言可能用到的寄存器的值到内核栈中,把栈中esp+36处的硬件出错码拷贝到edx寄存器中,并在这个位置填-1(硬件出错吗是),然后把esp+32出的C中断处理程序地址装入edi,在这个位置写入es的值。把栈顶地址保存在eax寄存器中。然后调用在edi中高级C函数
(2) 调用高级C语言处理异常
大部分的C异常处理程序都是把硬件出错码保存在当前进程的进程描述符中。然后给进程发送一个信号。异常处理程序还检查发生异常是在用户态或者内核态,如果是在内核态,那么说明内核有BUG,调用die函数,使得系统产生oops,并杀死当前进程。
(3) 从异常返回