源码
中断的应用
硬件中断响应
系统调用(SystemCall)
信号中断
系统的异常及错误
自定义中断
硬中断与软中断
硬中断:由硬件(如键盘、网卡)等产生,可以直接中断CPU。
软中断:由当前正在运行的进程产生(如I/O请求的发生),如执行到中断指令。
主要源码位置
源码位于kernel目录下的:
中断类型 | 中断前处理、中断后恢复 | 中断执行 |
---|---|---|
硬中断 | asm.s | trap.c |
软中断 | system_call.s | fork.c、signal.c、exit.c、sys.c |
硬中断初始化程序(traps.c)
参考:Linux0.11版本的set_trap_gate宏分析
trap_init
函数主要调用了set_trap_gate
和set_system_gate
两个函数来填写陷阱门(trap gate)的描述符。
其中set_trap_gate
的权限较高,而set_system_gate
的权限较低,可被所有用户调用。
冷知识:Liuns自己在此处写的注释是:/* int3-5 can be called from all */
这两个函数位于include/asm/system.h
下,定义如下:
可以看到其实这两个函数只是同一个函数_set_gate
的带参宏定义,参数不同,仅此而已。
_set_gate
的参数解析:
- 第一个位置的参数n代表此种中断在IDT数组的索引。
- 第二个位置的参数15即代表确认门类型为15(即01111,trap gate,陷阱门)。
- 第三个位置的参数(一个是0,一个是3)代表了DPL,此处分别是Ring0和Ring3。
- 第四个位置的参数addr为回调函数的地址。
_set_gate
函数体是一段内联汇编代码,如下,(具体是干什么用的):
硬中断处理流程(asm.s)
以下以divide_error
的处理流程为例。
1. 保存现场
保存现场指:
- SS栈堆寄存器、ESP、EFLAGS寄存器、CS、EIP。
- 通用寄存器如AX、BX等
在执行divide_error
段之前,已自动进行了保存现场的第一步(代码在哪里?),栈的分布情况如下:
栈底 |
---|
SS |
ESP |
EFLAGS |
CS |
EIP |
然后执行divide_erroe
段:
将中断服务程序的地址入栈
然后顺序执行no_error_code
段:
取ESP所指的内容,即中断服务程序的地址,与EAX的内容交换。
此步完成后,EAX中存了中断服务函数(do_divide_error
)的地址,而栈顶被替换为EAX的内容
接下来是10个入栈操作,执行完pushl $0
后栈的分布如下:
栈底 |
---|
SS |
ESP |
EFLAGS |
CS |
EIP |
EAX |
EBX |
ECX |
EDX |
EDI |
ESI |
EBP |
DS |
ES |
FS |
出错码(此种情况为0) |
然后是
取当前ESP值加上十进制的44B,即向栈底移动了11个位置处的内容,此地址处放的正好是EIP,即中断处理前的程序计数器值,将其入栈。
注意lea指令只是计算,而并不改变ESP的值
至此,所有入栈操作完成,保存现场结束,栈的分布如下:
栈底 |
---|
SS |
ESP |
EFLAGS |
CS |
EIP |
EAX |
EBX |
ECX |
EDX |
EDI |
ESI |
EBP |
DS |
ES |
FS |
出错码(此种情况为0) |
执行中断前的程序计数器值EIP |
2. 执行中断服务函数
EAX中放的是中断服务函数的入口地址,此种情况下是do_divide_error
,原型在traps.c中:
其将ESP、错误码作为参数传入后执行。
3. 恢复现场
堆栈指针下沉8B,即两个位置,跳过 出错码和执行中断前的EIP,因为这两项是供中断服务函数使用的,现在已经没用了。
跳过后,来到指向FS的位置,从FS到EAX依次出栈,还原所有的寄存器。
此步完成后栈的分布为:
栈底 |
---|
SS |
ESP |
EFLAGS |
CS |
EIP |
4. 中断返回
iret指令从栈中依次恢复EIP到SS。
至此结束。