[中断概述]
中断本质上是一种特殊的电信号,由硬件设备发向处理器。异常和中断的不同是异常在产生时必须考虑与处理器时钟同步。实际上异常也常常称为同步中断。比如在除0或者缺页时,必须靠内核处理的时候,处理器就会产生一个异常。
[中断处理机制的实现]
中断从硬件到内核的路由
设备产生中断,通过总线把电信号发送给中断控制器。如果中断线是激活的(它们允许被屏蔽的),那么中断控制器就会把中断发往处理器。在大多数体系结构中,这个工作就是通过电信号给处理器的特定管脚发送一个信号。除非在处理器上禁止该中断,否则处理器会立即停止它正在做的事,关闭中断系统,然后跳到内存中预定的位置开始执行那里的代码。这个预定义的位置是由内核设置的,是中断处理程序的入口点。
[中断处理程序]
在响应一个特定中断的时候,内核会执行一个函数,该函数叫做中断处理程序(interrupt handler)也叫做中断服务例程(interrupt service routine)。
中断处理程序就是一个普普通通的C函数,但是它与其他内核函数的真正区别在于,中断处理程序是被内核调用来响应中断的,它运行于中断上下文中。
注册中断处理程序:
static inline int __must_check
request_irq(unsigned int irq, irq_handler_thandler, unsigned long flags,
const char *name, void *dev)
irq:分配的中断号
handler:中断处理函数程序的指针 typedefirqreturn_t (*irq_handler_t)(int, void *);
flags:中断类型
name:与中断相关的设备的名字;
dev:用于共享中断,因为可能在一条中断线上有几个设备,dev用来区分是哪个设备产生的中断
中断上下文和进程上下文对比
|
中断上下文 |
进程上下文 |
定义 |
当执行一个中断处理程序时,内核处于中断上下文中 |
当程序调用了系统调用或者触发了某个异常,它就陷入了内核空间,此时,内核代表进程执行并处于进程上下文中。 |
睡眠情况 |
不可睡眠,不能被调度,也就是说中断上下文中不能使用有可能睡眠的函数 |
可睡眠,可调度 |
同步机制 |
自旋锁 |
都可 |
|
|
|
|
|
|
[中断上半部和下半部]
首先问自己一个问题,为什么要把中断分为上半部和下半部,难道一个就放在中断处理程序中不好吗?答案是否定的
中断的划分为我们解决了即想中断处理程序运行得快,又想中断处理程序完成的工作量多。
1、中断可以随时的打断其他正在执行的程序,如果被打断的代码对系统很重要,那么此时中断处理程序的执行时间应该是越短越好;
2、中断处理程序正在执行时,会屏蔽同条中断线上的中断请求;而更严重的是,如果设置了IRQF_DISABLED,那么该中断服务程序执行时会屏蔽所有其他的中断请求。那么此时应该让中断处理程序执行的越快越好。
上半部:一个快速、异步而简单的处理程序专门来负责对硬件的中断请求做出快速响应,与此同时也要完成那么些对时间要求很严格的操作;
下半部:那么对时间要求相对宽松,其他的剩余工作会在稍后的任意时间执行。
下面是对上半部和下半部的工作划分:
如果一个任务对时间非常敏感,将其放在中断处理程序中执行;
如果一个任务中和硬件相关,将其放在中断处理程序中执行;
如果一个任务要保证不被其他中断(特别是相同的中断)打断,将其放在中断处理程序执行;
其他所有任务,都应考虑放在下半部执行。
[下半部机制]
软中断
软中断保留给系统中对时间要求最严格以及最重要的下半部使用,目前只有两个子系统(网络和scsi)直接使用软中断。
Tasklet
Tasklet是利用软中断实现的一种下半部机制。在选择使用软中断还是tasklet时,建议使用tasklet,除了网络和SCSI情况。相比软中断,tasklet的接口更简单,锁保护要求较低。
工作队列
工作队列是可以把工作推后交由一个内核线程去执行,下半部总是会在进程上下文中执行,允许重新调度和睡眠。
三种下半部机制对比
下半部 |
上下文 |
顺序执行保障 |
软中断 |
中断 |
没有 |
Tasklet |
中断 |
同类型不能同时执行 |
工作队列 |
进程 |
没有(和进程上下文一样被调度) |