中断和异常
承接已有博客
中断:异步中断,由其他硬件设备依照CPU时钟信号随机产生,比如间隔定时器和IO设备
异常:同步中断,当指令执行时由CPU控制单元产生的,即程序的错误产生的
中断描述符表IDT
和异常或中断向量相关联,映射向量对应的异常或中断处理程序的入口地址
- 如何找到IDT:IDT在保护模式下可以在内存的任何位置,idtr寄存器里存放IDT的线性基地址及其限制
- Linux利用中断门(清IF标志,不响应中断请求)处理中断,利用陷阱门(不修改IF标志)处理异常
门描述符
IDT里的表项也叫门描述符
上半部
响应中断后关键而紧急的部分,内核立即执行,关中断执行,中断处理程序
中断处理程序
响应一个特定中断时,内核执行的函数
- 特点:
- 无需重入,因为Linux允许嵌套中断,但不允许同一中断处理程序嵌套
- 不允许睡眠(可以这样写,但理论上不应该允许睡眠),因为有时间限制
- 简单的划分标准
- 对时间非常敏感
- 和硬件相关
- 保证不被其他中断打断
下半部
响应中断后可以推迟的部分,内核随后执行,开中断执行。执行与中断处理密切相关但中断处理程序不执行的工作
- 原因:
- 中断处理程序(上半部)是异步执行,可能会打断其他重要进程,为了避免进程停止太久
- 中断处理程序执行时,会关中断,执行时间长会影响其他中断
- 处理时间:通常下半部在中断处理程序一返回就会马上运行
实现机制
软中断
静态定义的下半部接口,有32个
- 特点:
- 不允许睡眠
- 可以在所有处理器上同时执行,即便类型相同
- 需要自旋锁保护
- 采用单处理器数据
- 利用一些技巧避免显式加锁
- 必须是可重入函数,即函数执行过程被中断,返回时不会出现问题
- 用于性能需求比较高的情况
- 用于执行频率很高的情况
- 在编译期间静态注册
- 只有中断处理程序抢占软中断
- 执行时机
- 从上半部返回
- 显式检查待处理软中断
- 在ksoftirq内核线程中
- PS:一次完成固定数目的软中断,剩下的放入ksoftirq,避免用户进程饥饿得不到运行机会
tasklet
基于软中断(所以有软中断的基本特点)实现的的下半部接口,一般下半部使用tasklet
- 特点:
- 不允许睡眠
- 类型不同的tasklet可以在不同处理器上同时执行,但是类型相同的tasklet不能同时执行
- 可以通过代码动态注册
- 不必为可重入函数
工作队列
把工作推后,交由一个内核线程去执行——这个下半部分总会在进程上下文中执行
- 特点:
- 允许重新调度或者睡眠
- 这也是选择软中断tasklet还是选择工作队列需要考虑的
- 每个处理器对应一个内核线程
- 也是一次执行所有延迟工作
- 链表上有对象,状态设为TASK_RUNNING;链表上没有对象了,状态设为TASK_INTERRUPTIBLE
- 允许重新调度或者睡眠
do_softirq函数
处理固定个数的软中断,过量软中断交给ksoftirq线程
ksoftirqd内核线程
执行软中断的一个内核线程,每个CPU都有自己的
- 解决的问题:比如网卡上数据报泛滥会高频率激活软中断,即过量软中断造成用户进程饥饿无法运行
- 解决策略:do_softirq把过量的软中断交给ksoftirq线程
有关内核栈
- 如果内核栈是8KB,那么当前被中断进程的内核栈被用作中断栈
- 如果内核栈是4KB,内核就使用3种类型的内核栈
- 异常栈:用于处理异常,比如系统调用。共用内核栈
- 硬中断请求栈:用于处理中断,每个CPU一个,4KB
- 软中断请求栈:用于处理下半部,每个CPU一个,4KB