中断使能/禁止
IRQ 的控制在内核代码很多地方都有使用,主要是中断的禁止和使能的几组函数:
- local_irq_enable()
- local_irq_disable()
这两个通常以单个汇编指令实现,用于禁止/使能当前处理器的本地中断。
针对 ARM 处理器:
include/linux/irqflags.h
arch/arm/include/asm/irqflags.h
#define raw_local_irq_disable() arch_local_irq_disable()
#define raw_local_irq_enable() arch_local_irq_enable()
#define local_irq_enable() do { raw_local_irq_enable(); } while (0)
#define local_irq_disable() do { raw_local_irq_disable(); } while (0)
#define arch_local_irq_enable arch_local_irq_enable
static inline void arch_local_irq_enable(void)
{
asm volatile(
" cpsie i @ arch_local_irq_enable"
:
:
: "memory", "cc");
}
#define arch_local_irq_disable arch_local_irq_disable
static inline void arch_local_irq_disable(void)
{
asm volatile(
" cpsid i @ arch_local_irq_disable"
:
:
: "memory", "cc");
}
还有两个常用到的:
- local_irq_save(flags)
- local_irq_restore(flags)
这两个宏相对于local_irq_disable和local_irq_enable最大的区别在于,local_irq_save会在关闭中断前,将处理器当前的标志位保持在一个unsigned long flags中,在调用local_irq_restore时,在将保存的flags恢复到处理器的 flags 寄存器中。这样做是为了防止在一个关闭中断的环境中因为调用local_irq_disable和local_irq_enable破坏之前的中断响应状态。
注意:前面的调用,既可以在中断上下文中调用,也可以在进程上下文调用。
local_irq_enable() / local_irq_disable() / local_irq_save(flags) / local_irq_restore(flags)
禁止的是处理器的全局的中断,即全部禁止,如果希望禁止一条特定的中断(也就是 Mask 一个),则使用:
- void disable_irq(unsigned int irq)
- void enable_irq(unsigned int irq)
或者
- void disable_irq_nosync(unsigned int irq)
- void synchronize_irq(unsigned int irq)
他们可以禁止给定的中断向所有的处理器的传递。
disable_irq 的官方解释是:
This function waits for any pending IRQ handlers for this interrupt to complete before returning. If you use this function while holding a resource the IRQ handler may need you will deadlock. This function may be called - with care - from IRQ context
它需要等待被 pending 上的 IRQ 被处理完,才会返回!所以,官方给的说法是:“小心使用,否则死锁!”
如果在 IRQ Context 中使用这个,会导致死锁,死机,最后CPU 重启。
disable_irq_nosync 的官方解释是:
Unlike disable_irq(), this function does not ensure existing instances of the IRQ handler have completed before returning. This function may be called from IRQ context.
和 disable_irq 不一样,这个直接返回,所以能够在中断上下文调用。
enable_irq 是开启一条中断的 API,的官方解释是:
Undoes the effect of one call to disable_irq(). If this matches the last disable, processing of interrupts on this IRQ line is re-enabled. This function may be called from IRQ context only when desc->irq_data.chip->bus_lock and desc->chip->bus_sync_unlock are NULL !
有条件的在中断上下文使用,这里强调了一下,和 disable_irq 配对使用,什么意思呢?
比如你调用了 2 次 disable_irq() 或者 disable_irq_nosync() ,则下一次调用 enable_irq() 的时候,对应的中断并不会打开,需要在调用一次才行,也就是需要的嵌套配对。
synchronize_irq 也是开启一条中断的 API,的官方解释是:
This function waits for any pending IRQ handlers for this interrupt to complete before returning. If you use this function while holding a resource the IRQ handler may need you will deadlock. This function may be called - with care - from IRQ context.
所以,也是有等待的过程,所以中断上下文慎用!!
中断状态
irqs_disabled() 用于检测当前本地中断是否被禁止,若被禁止,返回非0,否则返回0
in_interrupt() 用来检测当前是否在中断上下文(很常用),若在中断上下文(或者在执行下半部),返回非0,否则返回0
如果代码希望做一些像睡眠这样的事情,可以调用这个函数,返回0,则说明是进程上下文。