本文是《Linux中断子系统》系列的第一篇,首先通过"妈妈怎么知道孩子醒了"这一生活场景类比,生动讲解了查询、休眠唤醒、poll、异步通知(中断)等几种事件通知方式,引出中断机制的核心意义。随后介绍了计算机系统中中断与异常的分类,以及Linux中断处理的三项核心原则:不允许嵌套、处理越快越好,以及将处理逻辑拆分为"上半部"与"下半部"的设计思想。本文适合刚开始接触Linux中断子系统的读者阅读。1 初识中断
1.1 中断机制的引入
1.1.1 妈妈怎么知道孩子醒了
妈妈怎么知道卧室里小孩醒了?
① 时不时进房间看一下:查询方式
简单,但是累
② 进去房间陪小孩一起睡觉,小孩醒了会吵醒她:休眠-唤醒
不累,但是妈妈干不了活了
③ 妈妈要干很多活,但是可以陪小孩睡一会,定个闹钟:poll方式
要浪费点时间,但是可以继续干活。
妈妈要么是被小孩吵醒,要么是被闹钟吵醒。
④ 妈妈在客厅干活,小孩醒了他会自己走出房门告诉妈妈:异步通知
妈妈、小孩互不耽误。
后面的3种方式,都需要"小孩来中断妈妈":中断她的睡眠、中断她的工作。
实际上,能"中断"妈妈的事情可多了:
① 远处的猫叫:这可以被忽略
② 门铃、小孩哭声:妈妈的应对措施不一样
③ 身体不舒服:那要赶紧休息
④ 有蜘蛛掉下来了:赶紧跑啊,救命
妈妈当前正在看书,被"中断"后她会怎么做?流程如下:
① 妈妈正在看书
② 发生了各种声音
可忽略的远处猫叫
快递员按门铃
卧室中小孩哭了
③ 妈妈怎么办?
a. 先在书中放入书签,合上书
b. 去处理
对于不同的情况,处理方法不同:
对于门铃:开门取快递
对于哭声:照顾小孩
c. 回来继续看书
1.2 计算机系统中的中断和异常
CPU在运行的过程中,也会被各种"异常"打断。这些"异常"有:① 指令未定义
② 指令、数据访问有问题
③ SWI(软中断)
④ 快中断
⑤ 中断
中断也属于一种"异常",导致中断发生的情况有很多,比如:
① 按键
② 定时器
③ ADC转换完成
④ UART发送完数据、收到数据
⑤ 等等
这些众多的"中断源",汇集到"中断控制器",由"中断控制器"选择优先级最高的中断并通知CPU。
1.3 中断处理过程
1.3.1 中断处理原则1:不能嵌套
中断A正在处理的过程中,假设又发生了中断B,那么在栈里要保存A的现场,然后处理B。
在处理B的过程中又发生了中断C,那么在栈里要保存B的现场,然后处理C。
如果中断嵌套突然暴发,那么栈将越来越大,栈终将耗尽。
所以,为了防止这种情况发生,也是为了简单化中断的处理,在Linux系统上中断无法嵌套:即当前中断A没处理完之前,不会响应另一个中断B(即使它的优先级更高)。
1.3.2 中断处理原则2:越快越好
妈妈在家中照顾小孩时,门铃响起,她开门取快递:这就是中断的处理。她取个快递敢花上半天吗?不怕小孩出意外吗?同理,在Linux系统中,中断的处理也是越快越好。
在单芯片系统中,假设中断处理很慢,那应用程序在这段时间内就无法执行:系统显得很迟顿。
在SMP系统中,假设中断处理很慢,那么正在处理这个中断的CPU上的其他线程也无法执行。
在中断的处理过程中,该CPU是不能进行进程调度的,所以中断的处理要越快越好,尽早让其他中断能被处理──进程调度靠定时器中断来实现。
在Linux系统中使用中断挺简单,为某个中断irq注册中断处理函数handler,可以使用request_irq函数:
在handler函数中,代码尽可能高效。
但是,处理某个中断要做的事情就是很多,没办法加快。比如对于按键中断,我们需要等待几十毫秒消除机械抖动。难道要在handler中等待吗?对于计算机来说,这可是一段很长的时间。
1.3.3 要处理的事情实在太多,拆分为:上半部、下半部
当一个中断要耗费很多时间来处理时,它的坏处是:在这段时间内,其他中断无法被处理。换句话说,在这段时间内,系统是关中断的。如果某个中断就是要做那么多事,我们能不能把它拆分成两部分:紧急的、不紧急的?
在handler函数里只做紧急的事,然后就重新开中断,让系统得以正常运行;那些不紧急的事,以后再处理,处理时是开中断的。
中断下半部的实现有很多种方法,主要有:tasklet(小任务)、work queue(工作队列)、中断线程化。