http://fanli7.net/a/bianchengyuyan/C__/20120725/192687.html
內核版本:2.6.30
平台:arm
在 單處理器不可搶占系統中,使用local_irq_enable和local_irq_disable是消除異步並發源的有效方式。在驅動程序中要避免使 用這兩個宏(系統不能長時間不響應中斷),後面將要介紹的自旋锁等互斥機制中會經常用到這兩個宏。local_irq_enable宏用於打開本地處理器 的中斷,local_irq_disable宏則用來關閉本處理器的中斷。這兩個宏的定義如下:
Linux/include/linux/irqflags.h
59 #define local_irq_enable() \
60 do { trace_hardirqs_on(); raw_local_irq_enable(); } while (0)
61 #define local_irq_disable() \
62 do { raw_local_irq_disable(); trace_hardirqs_off(); } while (0)
其中trace_hardirqs_on()和trace_hardirqs_off() 用於調試,這裏重點講解raw_local_irq_enable()和raw_local_irq_disable(),這兩個宏的具體實現都依賴於處 理器架構,不同的處理器有不同的指令來關閉或启用處理器響應外部中斷的能力。下面是ARM平台的實現:
Linux/arch/arm/include/asm/irqflags.h
8 /*
9 * CPU interrupt mask handling.
10 */
11 #if __LINUX_ARM_ARCH__ >= 6 //ARMv6以後的指令集,通過CPSIE指令完成
21 #define raw_local_irq_enable() __asm__("cpsie i @ __sti" : : : "memory", "cc")
22 #define raw_local_irq_disable() __asm__("cpsid i @ __cli" : : : "memory", "cc")
23 #define local_fiq_enable() __asm__("cpsie f @ __stf" : : : "memory", "cc")
24 #define local_fiq_disable() __asm__("cpsid f @ __clf" : : : "memory", "cc")
25
26 #else//ARMv6以前的指令集,S3C2440的指令集是ARMv4T,通過mrs和msr指令完成
27
28 /*
29 * Save the current interrupt enable state & disable IRQs
30 */
43
44 /*
45 * Enable IRQs
46 */
47 #define raw_local_irq_enable() \
48 ({ \
49 unsigned long temp; \
50 __asm__ __volatile__( \
51 "mrs %0, cpsr @ local_irq_enable\n" \
52 " bic %0, %0, #128\n" \
53 " msr cpsr_c, %0" \ //cpsr_c代表低8位
54 : "=r" (temp) \
55 : \
56 : "memory", "cc"); \
57 })
58
59 /*
60 * Disable IRQs
61 */
62 #define raw_local_irq_disable() \
63 ({ \
64 unsigned long temp; \
65 __asm__ __volatile__( \
66 "mrs %0, cpsr @ local_irq_disable\n" \
67 " orr %0, %0, #128\n" \
68 " msr cpsr_c, %0" \
69 : "=r" (temp) \
70 : \
71 : "memory", "cc"); \
72 })
103
104 #endif
在單處理器不可搶占系統中,如果某段代碼要訪問某共享資源,那麼在進入臨界區前使用local_irq_disable關閉中斷,這样在臨界區中可以保證不會出現異步並發源,訪問完成共享數據在出臨界區時,再調用local_irq_enable启用中斷。
local_irq_disable和local_irq_enable還有一種變體:local_irq_save和local_irq_restore,其定義如下:
Linux/include/linux/irqflags.h
63 #define local_irq_save(flags) \
64 do { \
65 typecheck(unsigned long, flags); \
66 raw_local_irq_save(flags); \
67 trace_hardirqs_off(); \
68 } while (0)
69
70
71 #define local_irq_restore(flags) \
72 do { \
73 typecheck(unsigned long, flags); \
74 if (raw_irqs_disabled_flags(flags)) { \
75 raw_local_irq_restore(flags); \
76 trace_hardirqs_off(); \
77 } else { \
78 trace_hardirqs_on(); \
79 raw_local_irq_restore(flags); \
80 } \
81 } while (0)
這兩個宏相對於local_irq_disable和local_irq_enable最大的區別在於,local_irq_save會在關閉中斷前,將處理器當前的標志位保持在一個unsigned long flags中,在調用local_irq_restore時,在將保存的flags恢复到處理器的FLAGS寄存器中。這样做是为了防止在一個關閉中斷的環境中因为調用local_irq_disable和local_irq_enable破壞之前的中斷響應狀態。
我們接下來再看一下raw_local_irq_save和raw_local_irq_save和raw_local_irq_restore的具體實現:
Linux/arch/arm/include/asm/irqflags.h
11 #if __LINUX_ARM_ARCH__ >= 6
12
13 #define raw_local_irq_save(x) \
14 ({ \
15 __asm__ __volatile__( \
16 "mrs %0, cpsr @ local_irq_save\n" \
17 "cpsid i" \
18 : "=r" (x) : : "memory", "cc"); \
19 })
26 #else
27
28 /*
29 * Save the current interrupt enable state & disable IRQs
30 */
31 #define raw_local_irq_save(x) \
32 ({ \
33 unsigned long temp; \
34 (void) (&temp == &x); \
35 __asm__ __volatile__( \
36 "mrs %0, cpsr @ local_irq_save\n" \
37 " orr %1, %0, #128\n" \
38 " msr cpsr_c, %1" \
39 : "=r" (x), "=r" (temp) \
40 : \
41 : "memory", "cc"); \
42 })
104 #endif
116 /*
117 * restore saved IRQ & FIQ state
118 */
119 #define raw_local_irq_restore(x) \
120 __asm__ __volatile__( \
121 "msr cpsr_c, %0 @ local_irq_restore\n" \
122 : \
123 : "r" (x) \
124 : "memory", "cc")
125
在單處理器不可搶占系統中,使用local_irq_disable和local_irq_enable及其變體對共享數據保護是簡單而有效的方法。但在使用時要注意,因为local_irq_disable和local_irq_enable是通過關閉中斷的方式進行互斥保護,所以必須確保處於兩者之間的代碼執行時間不能太長,否則將影響系統的性能。