zoukankan      html  css  js  c++  java
  • Linux内核同步原语之原子操作

    避免对同一数据的并发访问(通常由中断、对称多处理器、内核抢占等引起)称为同步。 

    ——题记

        内核源码:Linux-2.6.38.8.tar.bz2

        目标平台:ARM体系结构

        原子操作确保对同一数据的“读取-修改-写入”操作在它的执行期间不会被打断,要么全部执行完成,要么根本不会执行。例如在ARM上对全局变量的++运算至少要经历以下三步: 

    1. ldr r3, [r3, #0]  
    2. add r2, r3, #1  
    3. str r2, [r3, #0]  
    	ldr	r3, [r3, #0]
    	add	r2, r3, #1
    	str	r2, [r3, #0]

        这就给并发访问制造了可能,所以在编写内核代码时需要给有可能被并发访问的变量加上原子操作。

        Linux内核提供了两组原子操作接口,一组用于整数,一组用于位操作。

        1、原子整数操作

        针对整数的原子操作只能对atomic_t类型的数据进行处理。atomic_t定义如下: 

    1. /* linux-2.6.38.8/include/linux/types.h */  
    2. typedef struct {  
    3.     int counter;  
    4. } atomic_t;  
    /* linux-2.6.38.8/include/linux/types.h */
    typedef struct {
    	int counter;
    } atomic_t;
    

        下面将详细分析所有原子整数操作函数在ARMv6之前各种CPU上的实现(定义在linux-2.6.38.8/arch/arm/include/asm/atomic.h文件中)。 

    1. #define ATOMIC_INIT(i)  { (i) }  
    #define ATOMIC_INIT(i)	{ (i) }

        ATOMIC_INIT用于把atomic_t变量初始化为i,如下例把a初始化为2: 

    1. atomic_t a = ATOMIC_INIT(2);  
    atomic_t a = ATOMIC_INIT(2);
    1. #define atomic_read(v)  (*(volatile int *)&(v)->counter)  
    2. #define atomic_set(v,i) (((v)->counter) = (i))  
    #define atomic_read(v)	(*(volatile int *)&(v)->counter)
    #define atomic_set(v,i)	(((v)->counter) = (i))

        atomic_read(v)用于读取atomic_t变量*v的值,而atomic_set(v,i)用于把atomic_t变量*v设置为i。 

    1. static inline int atomic_add_return(int i, atomic_t *v)  
    2. {  
    3.     unsigned long flags;  
    4.     int val;  
    5.   
    6.     raw_local_irq_save(flags);  
    7.     val = v->counter;  
    8.     v->counter = val += i;  
    9.     raw_local_irq_restore(flags);  
    10.   
    11.     return val;  
    12. }  
    13. #define atomic_add(i, v)    (void) atomic_add_return(i, v)  
    static inline int atomic_add_return(int i, atomic_t *v)
    {
    	unsigned long flags;
    	int val;
    
    	raw_local_irq_save(flags);
    	val = v->counter;
    	v->counter = val += i;
    	raw_local_irq_restore(flags);
    
    	return val;
    }
    #define atomic_add(i, v)	(void) atomic_add_return(i, v)

        atomic_add(i, v)用于把atomic_t变量*v加i。这里的原子性实现只是简单地通过raw_local_irq_save函数来禁止中断。 

    1. /* linux-2.6.38.8/include/linux/irqflags.h */  
    2. #define raw_local_irq_save(flags)             
    3.     do {                          
    4.         typecheck(unsigned long, flags);      
    5.         flags = arch_local_irq_save();        
    6.     } while (0)  
    7. /* linux-2.6.38.8/arch/arm/include/asm/irqflags.h */  
    8. static inline unsigned long arch_local_irq_save(void)  
    9. {  
    10.     unsigned long flags, temp;  
    11.   
    12.     asm volatile(  
    13.         "   mrs %0, cpsr    @ arch_local_irq_save "  
    14.         "   orr %1, %0, #128 "  
    15.         "   msr cpsr_c, %1"  
    16.         : "=r" (flags), "=r" (temp)  
    17.         :  
    18.         : "memory", "cc");  
    19.     return flags;  
    20. }  
    /* linux-2.6.38.8/include/linux/irqflags.h */
    #define raw_local_irq_save(flags)			
    	do {						
    		typecheck(unsigned long, flags);	
    		flags = arch_local_irq_save();		
    	} while (0)
    /* linux-2.6.38.8/arch/arm/include/asm/irqflags.h */
    static inline unsigned long arch_local_irq_save(void)
    {
    	unsigned long flags, temp;
    
    	asm volatile(
    		"	mrs	%0, cpsr	@ arch_local_irq_save
    "
    		"	orr	%1, %0, #128
    "
    		"	msr	cpsr_c, %1"
    		: "=r" (flags), "=r" (temp)
    		:
    		: "memory", "cc");
    	return flags;
    }

        typecheck函数用来确保参数flags的数据类型为unsigned long,arch_local_irq_save函数通过内嵌汇编设置当前程序状态寄存器的I位为1,从而禁止IRQ。待加操作完成后,再通过raw_local_irq_restore函数使能中断。 

    1. static inline int atomic_sub_return(int i, atomic_t *v)  
    2. {  
    3.     unsigned long flags;  
    4.     int val;  
    5.   
    6.     raw_local_irq_save(flags);  
    7.     val = v->counter;  
    8.     v->counter = val -= i;  
    9.     raw_local_irq_restore(flags);  
    10.   
    11.     return val;  
    12. }  
    13. #define atomic_sub(i, v)    (void) atomic_sub_return(i, v)  
    static inline int atomic_sub_return(int i, atomic_t *v)
    {
    	unsigned long flags;
    	int val;
    
    	raw_local_irq_save(flags);
    	val = v->counter;
    	v->counter = val -= i;
    	raw_local_irq_restore(flags);
    
    	return val;
    }
    #define atomic_sub(i, v)	(void) atomic_sub_return(i, v)

        atomic_sub(i, v)用于把atomic_t变量*v减i。 

    1. #define atomic_inc(v)       atomic_add(1, v)  
    2. #define atomic_dec(v)       atomic_sub(1, v)  
    3.   
    4. #define atomic_inc_and_test(v)  (atomic_add_return(1, v) == 0)  
    5. #define atomic_dec_and_test(v)  (atomic_sub_return(1, v) == 0)  
    6. #define atomic_inc_return(v)    (atomic_add_return(1, v))  
    7. #define atomic_dec_return(v)    (atomic_sub_return(1, v))  
    8. #define atomic_sub_and_test(i, v) (atomic_sub_return(i, v) == 0)  
    9.   
    10. #define atomic_add_negative(i,v) (atomic_add_return(i, v) < 0)  
    #define atomic_inc(v)		atomic_add(1, v)
    #define atomic_dec(v)		atomic_sub(1, v)
    
    #define atomic_inc_and_test(v)	(atomic_add_return(1, v) == 0)
    #define atomic_dec_and_test(v)	(atomic_sub_return(1, v) == 0)
    #define atomic_inc_return(v)    (atomic_add_return(1, v))
    #define atomic_dec_return(v)    (atomic_sub_return(1, v))
    #define atomic_sub_and_test(i, v) (atomic_sub_return(i, v) == 0)
    
    #define atomic_add_negative(i,v) (atomic_add_return(i, v) < 0)

        这些函数只是上面所讲函数的封装。 

    1. static inline int atomic_cmpxchg(atomic_t *v, int old, int new)  
    2. {  
    3.     int ret;  
    4.     unsigned long flags;  
    5.   
    6.     raw_local_irq_save(flags);  
    7.     ret = v->counter;  
    8.     if (likely(ret == old))  
    9.         v->counter = new;  
    10.     raw_local_irq_restore(flags);  
    11.   
    12.     return ret;  
    13. }  
    14. static inline int atomic_add_unless(atomic_t *v, int a, int u)  
    15. {  
    16.     int c, old;  
    17.   
    18.     c = atomic_read(v);  
    19.     while (c != u && (old = atomic_cmpxchg((v), c, c + a)) != c)  
    20.         c = old;  
    21.     return c != u;  
    22. }  
    23. #define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0)  
    static inline int atomic_cmpxchg(atomic_t *v, int old, int new)
    {
    	int ret;
    	unsigned long flags;
    
    	raw_local_irq_save(flags);
    	ret = v->counter;
    	if (likely(ret == old))
    		v->counter = new;
    	raw_local_irq_restore(flags);
    
    	return ret;
    }
    static inline int atomic_add_unless(atomic_t *v, int a, int u)
    {
    	int c, old;
    
    	c = atomic_read(v);
    	while (c != u && (old = atomic_cmpxchg((v), c, c + a)) != c)
    		c = old;
    	return c != u;
    }
    #define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0)

        atomic_inc_not_zero(v)用于将atomic_t变量*v加1,并测试加1后的*v是否不为零,如果不为零则返回真。

        注意,这里所讲函数的参数v都是atomic_t类型数据的地址。

        2、原子位操作

        在CONFIG_SMP和__ARMEB__都未定义的情况下,原子位操作相关函数的定义如下: 

    1. /* linux-2.6.38.8/arch/arm/include/asm/bitops.h */  
    2. #define ATOMIC_BITOP_LE(name,nr,p)        
    3.     (__builtin_constant_p(nr) ?       
    4.      ____atomic_##name(nr, p) :       
    5.      _##name##_le(nr,p))  
    6.   
    7. #define set_bit(nr,p)           ATOMIC_BITOP_LE(set_bit,nr,p)  
    8. #define clear_bit(nr,p)         ATOMIC_BITOP_LE(clear_bit,nr,p)  
    9. #define change_bit(nr,p)        ATOMIC_BITOP_LE(change_bit,nr,p)  
    10. #define test_and_set_bit(nr,p)      ATOMIC_BITOP_LE(test_and_set_bit,nr,p)  
    11. #define test_and_clear_bit(nr,p)    ATOMIC_BITOP_LE(test_and_clear_bit,nr,p)  
    12. #define test_and_change_bit(nr,p)   ATOMIC_BITOP_LE(test_and_change_bit,nr,p)  
    /* linux-2.6.38.8/arch/arm/include/asm/bitops.h */
    #define	ATOMIC_BITOP_LE(name,nr,p)		
    	(__builtin_constant_p(nr) ?		
    	 ____atomic_##name(nr, p) :		
    	 _##name##_le(nr,p))
    
    #define set_bit(nr,p)			ATOMIC_BITOP_LE(set_bit,nr,p)
    #define clear_bit(nr,p)			ATOMIC_BITOP_LE(clear_bit,nr,p)
    #define change_bit(nr,p)		ATOMIC_BITOP_LE(change_bit,nr,p)
    #define test_and_set_bit(nr,p)		ATOMIC_BITOP_LE(test_and_set_bit,nr,p)
    #define test_and_clear_bit(nr,p)	ATOMIC_BITOP_LE(test_and_clear_bit,nr,p)
    #define test_and_change_bit(nr,p)	ATOMIC_BITOP_LE(test_and_change_bit,nr,p)

        相对应的非原子位操作函数定义在linux-2.6.38.8/include/asm-generic/bitops/non-atomic.h文件中,它们的名字前都多带有两个下划线,如set_bit的非原子操作函数为__set_bit。

         原子位操作函数根据参数nr是否为常量分为两种实现方式:

        (1)、nr为常量时 

    1. /* linux-2.6.38.8/arch/arm/include/asm/bitops.h */  
    2. static inline void ____atomic_set_bit(unsigned int bit, volatile unsigned long *p)  
    3. {  
    4.     unsigned long flags;  
    5.     unsigned long mask = 1UL << (bit & 31);  
    6.   
    7.     p += bit >> 5;  
    8.   
    9.     raw_local_irq_save(flags);  
    10.     *p |= mask;  
    11.     raw_local_irq_restore(flags);  
    12. }  
    13. static inline void ____atomic_clear_bit(unsigned int bit, volatile unsigned long *p)  
    14. {  
    15.     unsigned long flags;  
    16.     unsigned long mask = 1UL << (bit & 31);  
    17.   
    18.     p += bit >> 5;  
    19.   
    20.     raw_local_irq_save(flags);  
    21.     *p &= ~mask;  
    22.     raw_local_irq_restore(flags);  
    23. }  
    24. static inline void ____atomic_change_bit(unsigned int bit, volatile unsigned long *p)  
    25. {  
    26.     unsigned long flags;  
    27.     unsigned long mask = 1UL << (bit & 31);  
    28.   
    29.     p += bit >> 5;  
    30.   
    31.     raw_local_irq_save(flags);  
    32.     *p ^= mask;  
    33.     raw_local_irq_restore(flags);  
    34. }  
    /* linux-2.6.38.8/arch/arm/include/asm/bitops.h */
    static inline void ____atomic_set_bit(unsigned int bit, volatile unsigned long *p)
    {
    	unsigned long flags;
    	unsigned long mask = 1UL << (bit & 31);
    
    	p += bit >> 5;
    
    	raw_local_irq_save(flags);
    	*p |= mask;
    	raw_local_irq_restore(flags);
    }
    static inline void ____atomic_clear_bit(unsigned int bit, volatile unsigned long *p)
    {
    	unsigned long flags;
    	unsigned long mask = 1UL << (bit & 31);
    
    	p += bit >> 5;
    
    	raw_local_irq_save(flags);
    	*p &= ~mask;
    	raw_local_irq_restore(flags);
    }
    static inline void ____atomic_change_bit(unsigned int bit, volatile unsigned long *p)
    {
    	unsigned long flags;
    	unsigned long mask = 1UL << (bit & 31);
    
    	p += bit >> 5;
    
    	raw_local_irq_save(flags);
    	*p ^= mask;
    	raw_local_irq_restore(flags);
    }

        在32位机上,参数bit的理想取值为0到31,例如取值为1,就表示对*p的bit[1]进行操作。

        当bit取值大于31时,函数操作的就不是你想要操作的那个变量*p了(通过p += bit>> 5;语句实现),所以在实际的应用中应该要确保bit的取值在合理的范围内。

        *p |= mask;语句实现bit位的置位。

        *p &= ~mask;语句实现bit位的清零。

        *p ^= mask;语句实现bit位的翻转,即*p的bit位为0时被置位,为1时则被清零。 

    1. static inline int  
    2. ____atomic_test_and_set_bit(unsigned int bit, volatile unsigned long *p)  
    3. {  
    4.     unsigned long flags;  
    5.     unsigned int res;  
    6.     unsigned long mask = 1UL << (bit & 31);  
    7.   
    8.     p += bit >> 5;  
    9.   
    10.     raw_local_irq_save(flags);  
    11.     res = *p;  
    12.     *p = res | mask;  
    13.     raw_local_irq_restore(flags);  
    14.   
    15.     return (res & mask) != 0;  
    16. }  
    17. static inline int  
    18. ____atomic_test_and_clear_bit(unsigned int bit, volatile unsigned long *p)  
    19. {  
    20.     unsigned long flags;  
    21.     unsigned int res;  
    22.     unsigned long mask = 1UL << (bit & 31);  
    23.   
    24.     p += bit >> 5;  
    25.   
    26.     raw_local_irq_save(flags);  
    27.     res = *p;  
    28.     *p = res & ~mask;  
    29.     raw_local_irq_restore(flags);  
    30.   
    31.     return (res & mask) != 0;  
    32. }  
    33. static inline int  
    34. ____atomic_test_and_change_bit(unsigned int bit, volatile unsigned long *p)  
    35. {  
    36.     unsigned long flags;  
    37.     unsigned int res;  
    38.     unsigned long mask = 1UL << (bit & 31);  
    39.   
    40.     p += bit >> 5;  
    41.   
    42.     raw_local_irq_save(flags);  
    43.     res = *p;  
    44.     *p = res ^ mask;  
    45.     raw_local_irq_restore(flags);  
    46.   
    47.     return (res & mask) != 0;  
    48. }  
    static inline int
    ____atomic_test_and_set_bit(unsigned int bit, volatile unsigned long *p)
    {
    	unsigned long flags;
    	unsigned int res;
    	unsigned long mask = 1UL << (bit & 31);
    
    	p += bit >> 5;
    
    	raw_local_irq_save(flags);
    	res = *p;
    	*p = res | mask;
    	raw_local_irq_restore(flags);
    
    	return (res & mask) != 0;
    }
    static inline int
    ____atomic_test_and_clear_bit(unsigned int bit, volatile unsigned long *p)
    {
    	unsigned long flags;
    	unsigned int res;
    	unsigned long mask = 1UL << (bit & 31);
    
    	p += bit >> 5;
    
    	raw_local_irq_save(flags);
    	res = *p;
    	*p = res & ~mask;
    	raw_local_irq_restore(flags);
    
    	return (res & mask) != 0;
    }
    static inline int
    ____atomic_test_and_change_bit(unsigned int bit, volatile unsigned long *p)
    {
    	unsigned long flags;
    	unsigned int res;
    	unsigned long mask = 1UL << (bit & 31);
    
    	p += bit >> 5;
    
    	raw_local_irq_save(flags);
    	res = *p;
    	*p = res ^ mask;
    	raw_local_irq_restore(flags);
    
    	return (res & mask) != 0;
    }

        这三个函数增加了return (res & mask) != 0;语句,用来判断*p的bit位原值是否为1,如果为1则函数返回1,否则返回0。

        (2)、nr不为常量时

        当nr不为常量时,原子位操作函数的定义如下: 

    1. /* linux-2.6.38.8/arch/arm/include/asm/bitops.h */  
    2. extern void _set_bit_le(int nr, volatile unsigned long * p);  
    3. extern void _clear_bit_le(int nr, volatile unsigned long * p);  
    4. extern void _change_bit_le(int nr, volatile unsigned long * p);  
    5. extern int _test_and_set_bit_le(int nr, volatile unsigned long * p);  
    6. extern int _test_and_clear_bit_le(int nr, volatile unsigned long * p);  
    7. extern int _test_and_change_bit_le(int nr, volatile unsigned long * p);  
    /* linux-2.6.38.8/arch/arm/include/asm/bitops.h */
    extern void _set_bit_le(int nr, volatile unsigned long * p);
    extern void _clear_bit_le(int nr, volatile unsigned long * p);
    extern void _change_bit_le(int nr, volatile unsigned long * p);
    extern int _test_and_set_bit_le(int nr, volatile unsigned long * p);
    extern int _test_and_clear_bit_le(int nr, volatile unsigned long * p);
    extern int _test_and_change_bit_le(int nr, volatile unsigned long * p);

        它们都是通过汇编语言来实现的,定义如下: 

    1. /* linux-2.6.38.8/arch/arm/lib/setbit.S */  
    2. ENTRY(_set_bit_le)  
    3.     bitop   orr  
    4. ENDPROC(_set_bit_le)  
    5.   
    6. /* linux-2.6.38.8/arch/arm/lib/clearbit.S */  
    7. ENTRY(_clear_bit_le)  
    8.     bitop   bic  
    9. ENDPROC(_clear_bit_le)  
    10.   
    11. /* linux-2.6.38.8/arch/arm/lib/changebit.S */  
    12. ENTRY(_change_bit_le)  
    13.     bitop   eor  
    14. ENDPROC(_change_bit_le)  
    15.   
    16. /* linux-2.6.38.8/arch/arm/lib/testsetbit.S */  
    17. ENTRY(_test_and_set_bit_le)  
    18.     testop  orreq, streqb  
    19. ENDPROC(_test_and_set_bit_le)  
    20.   
    21. /* linux-2.6.38.8/arch/arm/lib/testclearbit.S */  
    22. ENTRY(_test_and_clear_bit_le)  
    23.     testop  bicne, strneb  
    24. ENDPROC(_test_and_clear_bit_le)  
    25.   
    26. /* linux-2.6.38.8/arch/arm/lib/testchangebit.S */  
    27. ENTRY(_test_and_change_bit_le)  
    28.     testop  eor, strb  
    29. ENDPROC(_test_and_change_bit_le)  
    /* linux-2.6.38.8/arch/arm/lib/setbit.S */
    ENTRY(_set_bit_le)
    	bitop	orr
    ENDPROC(_set_bit_le)
    
    /* linux-2.6.38.8/arch/arm/lib/clearbit.S */
    ENTRY(_clear_bit_le)
    	bitop	bic
    ENDPROC(_clear_bit_le)
    
    /* linux-2.6.38.8/arch/arm/lib/changebit.S */
    ENTRY(_change_bit_le)
    	bitop	eor
    ENDPROC(_change_bit_le)
    
    /* linux-2.6.38.8/arch/arm/lib/testsetbit.S */
    ENTRY(_test_and_set_bit_le)
    	testop	orreq, streqb
    ENDPROC(_test_and_set_bit_le)
    
    /* linux-2.6.38.8/arch/arm/lib/testclearbit.S */
    ENTRY(_test_and_clear_bit_le)
    	testop	bicne, strneb
    ENDPROC(_test_and_clear_bit_le)
    
    /* linux-2.6.38.8/arch/arm/lib/testchangebit.S */
    ENTRY(_test_and_change_bit_le)
    	testop	eor, strb
    ENDPROC(_test_and_change_bit_le)

        使用ENTRY和ENDPROC两个宏来定义一个名为name的函数,如下所示: 

    1. /* linux-2.6.38.8/include/linux/linkage.h */  
    2. #define ENTRY(name)   
    3.   .globl name;   
    4.   ALIGN;   
    5.   name:  
    6.   
    7. #define ALIGN __ALIGN  
    8.   
    9. #define END(name)   
    10.   .size name, .-name  
    11.   
    12. /* linux-2.6.38.8/arch/arm/include/asm/linkage.h */  
    13. #define __ALIGN .align 0  
    14.   
    15. #define ENDPROC(name)   
    16.   .type name, %function;   
    17.   END(name)  
    /* linux-2.6.38.8/include/linux/linkage.h */
    #define ENTRY(name) 
      .globl name; 
      ALIGN; 
      name:
    
    #define ALIGN __ALIGN
    
    #define END(name) 
      .size name, .-name
    
    /* linux-2.6.38.8/arch/arm/include/asm/linkage.h */
    #define __ALIGN .align 0
    
    #define ENDPROC(name) 
      .type name, %function; 
      END(name)

        而汇编代码实现的宏bitop和testop被相应函数所调用,并传递给它们相应的参数,代码如下所示: 

    1. /* linux-2.6.38.8/arch/arm/lib/bitops.h */  
    2.     .macro  bitop, instr  
    3.     and r2, r0, #7  
    4.     mov r3, #1  
    5.     mov r3, r3, lsl r2  
    6.     save_and_disable_irqs ip  
    7.     ldrb    r2, [r1, r0, lsr #3]  
    8.     instr  r2, r2, r3  
    9.     strb    r2, [r1, r0, lsr #3]  
    10.     restore_irqs ip  
    11.     mov pc, lr  
    12.     .endm  
    13.   
    14.     .macro  testop, instr, store  
    15.     add r1, r1, r0, lsr #3  
    16.     and r3, r0, #7  
    17.     mov r0, #1  
    18.     save_and_disable_irqs ip  
    19.     ldrb    r2, [r1]  
    20.     tst r2, r0, lsl r3  
    21.     instr  r2, r2, r0, lsl r3  
    22.     store  r2, [r1]  
    23.     moveq   r0, #0  
    24.     restore_irqs ip  
    25.     mov pc, lr  
    26.     .endm  
  • 相关阅读:
    C#使用WINDOW
    赵四小姐从十六岁开始跟张学良。跟一年,属奸情;跟三年,算偷情;跟六十年,便成为千古爱情!
    Microsoft Visual Studio 2010(VS2010)正式版 CDKEY / SN:
    C#中byte[]与string的转换
    sqlserver waitfor time '10:00' waitfor delay '1:00' 时间延时 和 间隔
    免费下载 精英讲解
    在决定使用ClickOnce发布你的软件前,应该知道的一些事情
    Windows7下注册OCX的注意事项
    用命令行以最快速简单的方式搭建MySQL数据库
    设计模式探索系列之Bridge模式
  • 原文地址:https://www.cnblogs.com/zxc2man/p/6649747.html
Copyright © 2011-2022 走看看