local_irq_enable和local_irq_disable
在单处理器不可抢占系统中,使用local_irq_enable与local_irq_disable是消除异步并发源的有效方式,虽然驱动程序中应该避免使用这两个宏(理山将在本章稍后的内容中给出),但是在spinlock等互斥机制中常常用到这两个宏,所以在此用一节的篇幅来对它们进行介绍。local_irq_enable宏用来打开本地处理器的中断,而local_irq_disable则正好相反,用来关闭处理器的中断。这两个宏的定义如下:
#define local_irq_enable() \ do { trace_hardirqs_on(); raw_local_irq_enable(); } while (0) #define local_irq_disable() \ do { raw_local_irq_disable(); trace_hardirqs_off(); } while (0)
其中trace_hardirqs_on()和trace_hardirqs_off()用做调试,这里重点关注raw_local_irq_enable()和raw_local_irq_disable()。这两个宏的具体实现都依赖于处理器体系架构,不同处理器有不同的指令来启用或者关闭处理器响应外部中断的能力,比如在x86平台上,会最终利用sti和cli指令来分别设置和清除x86处理器中的FLAGS寄存器的IF标志,这样处理器就可以响应或者不响应外部的中断。ARM平台则使用CPSIE指令。
在单处理器不可抢占系统中,如果某段代码要访问某共享资源,那么在进入临界区前使用local_irq_disable来关闭中断,这样在临界区中可保证系统不会出现异步并发源,访问完共享数据在出临界区时,再调用local_irq_enable来启用中断。
local_irq_save与local_irq_restore
local_irq_enable与local_irq_disable还有一种变体,是local_irq_save与local_irq_restore宏定义如下:
#define local_irq_save(flags) \ do { \ raw_local_irq_save(flags); \ trace_hardirqs_off(); \ } while (0) #define local_irq_restore(flags) \ do { \ if (raw_irqs_disabled_flags(flags)) { \ raw_local_irq_restore(flags); \ trace_hardirqs_off(); \ } else { \ trace_hardirqs_on(); \ raw_local_irq_restore(flags); \ } \ } while (0)
这两个宏相对于local_irq_enable与local_irq_disable最大的不同在于,local_irq_save会在关闭中断前,将处理器当前的标志位保存在一个unsigned long flags中,在调用local_irq_restore的时候,再将保存的fla恢复到处理器的FLAGS寄存器中。这样做的目的是,防止在一个中断关闭的环境中因为调用local_irq_disable与local_irq_enable将之前的中断响应状态破坏掉。
在单处理器不可抢占系统中,使用local_irq_enable与local_irq_disable及其变体来对共享数据保护是种简单而有效的方法。但在使用时应该注意,因为local_irq_enable与local_irq_disable是通过关中断的方式进行互斥保护,所以必须确保处于两者之间的代码执行时间不能太长,否则将影响到系统的性能。