Linux内核设计与实现——7 中断与中断处理(8)——中断控制

Linux内核提供了一组接口用于操作机器上的中断状态。这些接口为我们提供了能够禁止当前处理器的中断系统,或屏蔽掉整个机器的一条中断线的能力。这些例程都是与体系无关的,可以在和中找到。
一般来说控制中断系统的原因归根结底是需要提供同步。通过禁止中断,可以确保某个中断处理程序不会抢占当前的代码。此外,禁止中断还可以禁止内核抢占。
锁提供的保护机制,防止来自其它处理器的并发访问,而禁止中断提供的保护机制,则是防止其它中断处理程序的并发访问。

1) 操作当前处理器的本地中断:
禁止:local_irq_disable()
激活:local_irq_enable()
如果在调用local_irq_disable()之前已经禁止了中断,那么往往会带来潜在的危险,同样,响应的local_irq_enable()也存在潜在的危险。因此,禁止中断之前保存中断系统的状态会更加安全一些,相反,在准备激活中断时,只需要把中断恢复到他们之前的状态。
unsigned longflags;
local_irq_save(flags); // 保存状态:
……//禁止中断
local_irq_restore(flags); //恢复状态

对于local_irq_save(flags)和local_irq_restore(flags)的调用必须在同一个函数中进行。

2)以前的内核中提供了一种“能够禁止系统中所有处理器上的中断”方法,这个函数是cli(),响应的激活函数是sli()。这些接口在2.5版本开发期间被取消了。取消全局cli()有不少优点。首先,强制驱动程序编写者实现真正的加锁。要知道具有特定目的的细粒度锁比全局锁要快许多,而且也完全吻合cli()的设计初衷。其次,这也使得很多代码更具流线型,避免了代码的成簇布局。所以,由此得到的中断系统更简单也更易于理解。

3)在某些情况下,我们只需要禁止整个系统的一条特定的中断线就够。linux提供了4个接口:
void disable_irq(unsignint_irq); //禁用中断线, 等待
void disable_irq_nosync(unsign int_irq); //禁用中断线,不等待
void ensable_irq(unsignint_irq); //激活中断线
void synchronize_irq(unsign int_irq); //等待特定中断处理程序退出

前两个函数禁止中断控制器上指定的中断线,即禁止指定的中断向系统中所有处理器传递。其中,第一个函数只有在当前正在执行的所有处理程序(该中断线的中断处理程序)完成后,才会返回。而第二个函数则直接返回,不进行等待。第四个函数用于等待一个特定的中断处理程序的退出。
这些函数的调用可以嵌套。但记住,每调用一次禁用就要调用一次激活,只有当最后一次激活被调用后,这条中断线才真正地被激活了。
禁止多个中断处理程序共享的中断线是不合适的。因为这样就禁止了这条线上得所有设备的中断传递。因此新的驱动程序倾向于不使用这些接口。

4)
irq_disable()查询中断状态,是禁止还是激活。如果本地处理器上的中断系统被禁止,它返回非0,否则返回0

in_interrupt(),如果内核处于任何类型的中断处理中(包括中断处理程序和下半部),它返回非0,否则返回0

in_irq()只有内核确实在执行中断处理程序(不包含下半部),才返回非0.

【Linux内核设计与实现——7 中断与中断处理(8)——中断控制】如果in_interrupt()返回0,则此刻内核处于进程上下文中。

    推荐阅读