一般来说,控制中断系统的原因归根到底是需要提供同步。
6.7.1 禁止和激活中断
用于禁止当前处理器(仅仅是当前处理器)上的本地中断,随后又激活它们的语句为:
- local_irq_disable();
- load_irq_enable();
如果在调用local_irq_disable()例程之前已经禁止了中断,那么该例程往往会带来潜在的危险;同样相应的load_irq_enable()例程也存在潜在危险,因为它将无条件地激活中断,尽管这些中断可能在开始时就是关闭的。所以我们需要一种机制把中断恢复到以前的状态而不是简单地禁止或激活。内核普遍关心这点,是因为内核中一个给定的代码路径既可以在中断激活的情况下达到也可以在中断禁止的情况下达到,这取决于具体的调用链。在禁止中断之前保存中断系统的状态会更加安全一些。相反,在准备激活中断时,只需把中断恢复到它们原来的状态。
- unsigned long flags;
- local_irq_save(flags);
- local_irq_restore(flags);
这些方法至少部分要以宏的形式实现,因此表面上flags参数是以值传递的。该参数包含具体系统结构的数据,也就是包含中断系统的状态。至少有一种体系结构把栈信息与值相结合,因此flags不能传递另一个函数(特别是它必须驻留在同一栈帧中)。基于这个原因,对local_irq_save()的调用和对local_irq_restore(flags)的调用必须在同一个函数中进行。
前面的所有函数既可以在中断中调用,也可以在进程上下文中调用。
不再使用全局的cli()
6.7.2 禁止指定中断线
对中断的状态操作之前禁止设备中断的传递。为此,Linux提供了四个接口:
- void disable_irq(unsigned int irq);
- void disable_irq_nosync(unsigned int irq);
- void enable_irq(unsigned int irq);
- void synchronize_irq(unsigned int irq);
前两个函数禁止中断控制器上指定的中断线,即禁止给定中断向系统中所有处理器的传递。另外,函数只有在当前正在执行的所有处理程序完成后,disable_irq()才能返回。函数disable_irq_nosync()不会等待当前中断处理程序执行完毕。
函数synchronize_irq()等待一个特定的中断处理程序的退出。如果该处理程序正在执行,那么该函数必须退出后才能返回。
对这些函数的调用可以嵌套。但要记住一条指定的中断线上,对disable_irq()或disable_irq_nosync()的每次调用,都需要相应地调用一次enable_irq()。只有在对enable_irq()完成最后一次调用后,才真正重新激活了中断线。例如,如果disable_irq()被调用了两次,那么直到第二次调用enable_irq()后,才能真正地激活中断线。
6.7.3 中断系统的状态
通常有必要了解中断系统的状态(例如中断时禁止的还是激活的),或者你当前是否正处于中断上下文的执行状态中。
宏irqs_disable()定义在<asm/system.h>中。如果本地处理器上的中断系统被禁止,则它返回非0,否则,返回0。
在<asm/hardirq.h>中定义的两个宏提供了一个用来检查内核的当前上下文的接口,它们是:
- in_interrupt()
- in_irq()
第一个宏最有用:如果内核处于中断上下文中,它返回非0。宏in_irq()只有在内核确实正在执行中断处理程序时才返回非0。