内核同步方法之原子操作

   原子操作可以保证指令以原子的方式执行,执行过程不被打断。

  内核提供了两组原子操作接口:一组针对整数进行操作,另一组针对单独的位进行操作。大多数体系结构要么本来就支持简单的原子操作,要么就为单步执行提供了锁内存总线的指令。

  • 原子整数操作
    针对整数的原子操作只能对atomic_t类型的数据进行处理。引入该类型的原因:

首先,让原子函数只接收atomic_t类型的操作数,可以确保原子操作只与这种特殊类型数据一起使用。保证了该类型数据不会被传递给其他任何非原子函数。

其次,使用atomic_t类型确保编译器不对相应的值进行访问优化,这使得原子操作最终接收到正确的内存地址而不是一个别名。

最后,在不同的体系结构上实现原子操作的时候,使用atomic_t类型可以屏蔽其间的差异。例如:

 

  1. 在《Atomic.h(asm-i386)》中
  2. /*
  3.  * Make sure gcc doesn't try to be clever and move things around
  4.  * on us. We need to use _exactly_ the address the user gave us,
  5.  * not some alias that contains the same information.
  6.  */
  7. typedef struct { int counter; } atomic_t;
  8. 在《Atomic.h(asm-sparc)》中
  9. typedef struct { volatile int counter; } atomic_t;
  10. /* This is the old 24-bit implementation.  It's still used internally
  11.  * by some sparc-specific code, notably the semaphore implementation.
  12.  */
  13. typedef struct { volatile int counter; } atomic24_t;

the old 24-bit implementation:

在SPARC体系结构上,原子操作的实现不同于其他体系结构:32位int类型的低8位嵌入一个锁,因为SPARC体系结构对原子操作缺乏指令级的支持,所以只能利用该锁来避免对原子类型数据的并发访问。

 

  1. 在<Atomic.h(asm-sparc)>中
  2. /* This is the old 24-bit implementation.  It's still used internally
  3.  * by some sparc-specific code, notably the semaphore implementation.
  4.  */
  5. typedef struct { volatile int counter; } atomic24_t;
  6. #ifndef CONFIG_SMP 
  7. #define ATOMIC24_INIT(i)  { (i) } 
  8. #define atomic24_read(v)          ((v)->counter) 
  9. #define atomic24_set(v, i)        (((v)->counter) = i) 
  10. #else 
  11. /* We do the bulk of the actual work out of line in two common
  12.  * routines in assembler, see arch/sparc/lib/atomic.S for the
  13.  * "fun" details.
  14.  *
  15.  * For SMP the trick is you embed the spin lock byte within
  16.  * the word, use the low byte so signedness is easily retained
  17.  * via a quick arithmetic shift.  It looks like this:
  18.  *
  19.  *  ----------------------------------------
  20.  *  | signed 24-bit counter value |  lock  |  atomic_t
  21.  *  ----------------------------------------
  22.  *   31                                      8 7      0
  23.  */
  24. #define ATOMIC24_INIT(i)    { ((i) << 8) }

相关操作:

 

知道什么叫欲哭无泪吗?我现在就是。早上写了上面的部分并保存,下午继续把这篇笔记写完了。但是可恶的CSDN不知道为啥只能发表上面的部分,下午写的都没有了!!我现在是一肚子悔恨啊,我已经不是第一次遇到这样的情况了,但是我居然只是拷贝没有把写好的粘贴到word里,后来有拷贝了其他东西到缓冲区就把写好的东西清空了,呜呜,现在不得不再重新写一次!!我的肠子现在都悔青了!!!

 

 

  1. 在<Atomic.h(include/asm-i386)>
  2. /*
  3.  * Atomic operations that C can't guarantee us.  Useful for
  4.  * resource counting etc..
  5.  */
  6. /*
  7.  * Make sure gcc doesn't try to be clever and move things around
  8.  * on us. We need to use _exactly_ the address the user gave us,
  9.  * not some alias that contains the same information.
  10.  */
  11. typedef struct { int counter; } atomic_t;
  12. #define ATOMIC_INIT(i)  { (i) }
  13. /**
  14.  * atomic_read - read atomic variable
  15.  * @v: pointer of type atomic_t
  16.  * 
  17.  * Atomically reads the value of @v.
  18.  */ 
  19. #define atomic_read(v)      ((v)->counter)
  20. /**
  21.  * atomic_set - set atomic variable
  22.  * @v: pointer of type atomic_t
  23.  * @i: required value
  24.  * 
  25.  * Atomically sets the value of @v to @i.
  26.  */ 
  27. #define atomic_set(v,i)     (((v)->counter) = (i))
  28. /**
  29.  * atomic_add - add integer to atomic variable
  30.  * @i: integer value to add
  31.  * @v: pointer of type atomic_t
  32.  * 
  33.  * Atomically adds @i to @v.
  34.  */
  35. static __inline__ void atomic_add(int i, atomic_t *v)
  36. {
  37.     __asm__ __volatile__(
  38.         LOCK_PREFIX "addl %1,%0"
  39.         :"+m" (v->counter)
  40.         :"ir" (i));
  41. }
  42. /**
  43.  * atomic_sub - subtract the atomic variable
  44.  * @i: integer value to subtract
  45.  * @v: pointer of type atomic_t
  46.  * 
  47.  * Atomically subtracts @i from @v.
  48.  */
  49. static __inline__ void atomic_sub(int i, atomic_t *v)
  50. {
  51.     __asm__ __volatile__(
  52.         LOCK_PREFIX "subl %1,%0"
  53.         :"+m" (v->counter)
  54.         :"ir" (i));
  55. }
  56. /**
  57.  * atomic_sub_and_test - subtract value from variable and test result
  58.  * @i: integer value to subtract
  59.  * @v: pointer of type atomic_t
  60.  * 
  61.  * Atomically subtracts @i from @v and returns
  62.  * true if the result is zero, or false for all
  63.  * other cases.
  64.  */
  65. static __inline__ int atomic_sub_and_test(int i, atomic_t *v)
  66. {
  67.     unsigned char c;
  68.     __asm__ __volatile__(
  69.         LOCK_PREFIX "subl %2,%0; sete %1"
  70.         :"+m" (v->counter), "=qm" (c)
  71.         :"ir" (i) : "memory");
  72.     return c;
  73. }
  74. /**
  75.  * atomic_inc - increment atomic variable
  76.  * @v: pointer of type atomic_t
  77.  * 
  78.  * Atomically increments @v by 1.
  79.  */ 
  80. static __inline__ void atomic_inc(atomic_t *v)
  81. {
  82.     __asm__ __volatile__(
  83.         LOCK_PREFIX "incl %0"
  84.         :"+m" (v->counter));
  85. }
  86. /**
  87.  * atomic_dec - decrement atomic variable
  88.  * @v: pointer of type atomic_t
  89.  * 
  90.  * Atomically decrements @v by 1.
  91.  */ 
  92. static __inline__ void atomic_dec(atomic_t *v)
  93. {
  94.     __asm__ __volatile__(
  95.         LOCK_PREFIX "decl %0"
  96.         :"+m" (v->counter));
  97. }
  98. /**
  99.  * atomic_dec_and_test - decrement and test
  100.  * @v: pointer of type atomic_t
  101.  * 
  102.  * Atomically decrements @v by 1 and
  103.  * returns true if the result is 0, or false for all other
  104.  * cases.
  105.  */ 
  106. static __inline__ int atomic_dec_and_test(atomic_t *v)
  107. {
  108.     unsigned char c;
  109.     __asm__ __volatile__(
  110.         LOCK_PREFIX "decl %0; sete %1"
  111.         :"+m" (v->counter), "=qm" (c)
  112.         : : "memory");
  113.     return c != 0;
  114. }
  115. /**
  116.  * atomic_inc_and_test - increment and test 
  117.  * @v: pointer of type atomic_t
  118.  * 
  119.  * Atomically increments @v by 1
  120.  * and returns true if the result is zero, or false for all
  121.  * other cases.
  122.  */ 
  123. static __inline__ int atomic_inc_and_test(atomic_t *v)
  124. {
  125.     unsigned char c;
  126.     __asm__ __volatile__(
  127.         LOCK_PREFIX "incl %0; sete %1"
  128.         :"+m" (v->counter), "=qm" (c)
  129.         : : "memory");
  130.     return c != 0;
  131. }
  132. /**
  133.  * atomic_add_negative - add and test if negative
  134.  * @v: pointer of type atomic_t
  135.  * @i: integer value to add
  136.  * 
  137.  * Atomically adds @i to @v and returns true
  138.  * if the result is negative, or false when
  139.  * result is greater than or equal to zero.
  140.  */ 
  141. static __inline__ int atomic_add_negative(int i, atomic_t *v)
  142. {
  143.     unsigned char c;
  144.     __asm__ __volatile__(
  145.         LOCK_PREFIX "addl %2,%0; sets %1"
  146.         :"+m" (v->counter), "=qm" (c)
  147.         :"ir" (i) : "memory");
  148.     return c;
  149. }

原子整数操作最常见的用途就是实现计数器。使用锁机制来保护一个单纯的计数器是笨拙的,开发者最好使用atomic_dec()和atmic_inc()。

原子操作通常是内联函数,往往是通过内嵌汇编指令来实现的。关于内嵌汇编指令的说明请参看:

http://blog.csdn.net/zdfonline/archive/2005/03/17/321904.aspx

如果某个函数本来就是原子的,那么它往往会被定义成一个宏。

  • 原子性与顺序性

原子性确保指令执行期间不被打断,要么全部执行完,要么根本不执行。一个字长的读取总是原子地发生,决不可能对同一个字交错地进行写;读总是返回一个完整的字。

顺序性确保即使两条或多条指令出现在独立的执行线程中,甚至独立的处理器上,但它们本该的执行顺序依然要保持。顺序性通过屏障(barrier)指令来实施。

 

  • 原子位操作

位操作函数是对普通的内存地址进行操作的。它的参数是一个指针和一个位号,第0位是给定地址的最低有效位(LSB)。在32位机上,第31位是给定地址的最高有效位而第32位是下一个字的最低有效位(LSB)。即

 bit 0 is the LSB of addr; bit 32 is the LSB of (addr+1).

使用原子位操作在多数情况下是对益而高字长的内存进行访问,因而位号该位于0~31之间,但是对位号的范围并没有限制。

由于原子位操作是对普通的指针进行的操作,所以不像原子整形对应atomic_t,这里没有特殊的数据类型。只要指针指向了任何数据,就可以对它进行操作。

相关操作:

 

  1. 在<Bitops.h(include/i386)>中
  2. /*
  3.  * These have to be done with inline assembly: that way the bit-setting
  4.  * is guaranteed to be atomic. All bit operations return 0 if the bit
  5.  * was cleared before the operation and != 0 if it was not.
  6.  *
  7.  * bit 0 is the LSB of addr; bit 32 is the LSB of (addr+1).
  8.  */
  9. #define ADDR (*(volatile long *) addr)
  10. /**
  11.  * set_bit - Atomically set a bit in memory
  12.  * @nr: the bit to set
  13.  * @addr: the address to start counting from
  14.  *
  15.  * This function is atomic and may not be reordered.  See __set_bit()
  16.  * if you do not require the atomic guarantees.
  17.  *
  18.  * Note: there are no guarantees that this function will not be reordered
  19.  * on non x86 architectures, so if you are writting portable code,
  20.  * make sure not to rely on its reordering guarantees.
  21.  *
  22.  * Note that @nr may be almost arbitrarily large; this function is not
  23.  * restricted to acting on a single-word quantity.
  24.  */
  25. static inline void set_bit(int nr, volatile unsigned long * addr)
  26. {
  27.     __asm__ __volatile__( LOCK_PREFIX
  28.         "btsl %1,%0"
  29.         :"+m" (ADDR)
  30.         :"Ir" (nr));
  31. }
  32. /**
  33.  * clear_bit - Clears a bit in memory
  34.  * @nr: Bit to clear
  35.  * @addr: Address to start counting from
  36.  *
  37.  * clear_bit() is atomic and may not be reordered.  However, it does
  38.  * not contain a memory barrier, so if it is used for locking purposes,
  39.  * you should call smp_mb__before_clear_bit() and/or smp_mb__after_clear_bit()
  40.  * in order to ensure changes are visible on other processors.
  41.  */
  42. static inline void clear_bit(int nr, volatile unsigned long * addr)
  43. {
  44.     __asm__ __volatile__( LOCK_PREFIX
  45.         "btrl %1,%0"
  46.         :"+m" (ADDR)
  47.         :"Ir" (nr));
  48. }
  49. /**
  50.  * change_bit - Toggle a bit in memory
  51.  * @nr: Bit to change
  52.  * @addr: Address to start counting from
  53.  *
  54.  * change_bit() is atomic and may not be reordered. It may be
  55.  * reordered on other architectures than x86.
  56.  * Note that @nr may be almost arbitrarily large; this function is not
  57.  * restricted to acting on a single-word quantity.
  58.  */
  59. static inline void change_bit(int nr, volatile unsigned long * addr)
  60. {
  61.     __asm__ __volatile__( LOCK_PREFIX
  62.         "btcl %1,%0"
  63.         :"+m" (ADDR)
  64.         :"Ir" (nr));
  65. }
  66. /**
  67.  * test_and_set_bit - Set a bit and return its old value
  68.  * @nr: Bit to set
  69.  * @addr: Address to count from
  70.  *
  71.  * This operation is atomic and cannot be reordered.  
  72.  * It may be reordered on other architectures than x86.
  73.  * It also implies a memory barrier.
  74.  */
  75. static inline int test_and_set_bit(int nr, volatile unsigned long * addr)
  76. {
  77.     int oldbit;
  78.     __asm__ __volatile__( LOCK_PREFIX
  79.         "btsl %2,%1/n/tsbbl %0,%0"
  80.         :"=r" (oldbit),"+m" (ADDR)
  81.         :"Ir" (nr) : "memory");
  82.     return oldbit;
  83. }
  84. /**
  85.  * test_and_clear_bit - Clear a bit and return its old value
  86.  * @nr: Bit to clear
  87.  * @addr: Address to count from
  88.  *
  89.  * This operation is atomic and cannot be reordered.
  90.  * It can be reorderdered on other architectures other than x86.
  91.  * It also implies a memory barrier.
  92.  */
  93. static inline int test_and_clear_bit(int nr, volatile unsigned long * addr)
  94. {
  95.     int oldbit;
  96.     __asm__ __volatile__( LOCK_PREFIX
  97.         "btrl %2,%1/n/tsbbl %0,%0"
  98.         :"=r" (oldbit),"+m" (ADDR)
  99.         :"Ir" (nr) : "memory");
  100.     return oldbit;
  101. }
  102. /**
  103.  * test_and_change_bit - Change a bit and return its old value
  104.  * @nr: Bit to change
  105.  * @addr: Address to count from
  106.  *
  107.  * This operation is atomic and cannot be reordered.  
  108.  * It also implies a memory barrier.
  109.  */
  110. static inline int test_and_change_bit(int nr, volatile unsigned long* addr)
  111. {
  112.     int oldbit;
  113.     __asm__ __volatile__( LOCK_PREFIX
  114.         "btcl %2,%1/n/tsbbl %0,%0"
  115.         :"=r" (oldbit),"+m" (ADDR)
  116.         :"Ir" (nr) : "memory");
  117.     return oldbit;
  118. }

为了方便,内核还提供了一组与上述操作相对应的非原子位函数。非原子位函数与原子位函数的操作完全相同,但是前者不保证原子性,代码里没有LOCK_PREFIX,其名字前缀多了两个下划线:

 

  1. /**
  2.  * __set_bit - Set a bit in memory
  3.  * @nr: the bit to set
  4.  * @addr: the address to start counting from
  5.  *
  6.  * Unlike set_bit(), this function is non-atomic and may be reordered.
  7.  * If it's called on the same region of memory simultaneously, the effect
  8.  * may be that only one operation succeeds.
  9.  */
  10. static inline void __set_bit(int nr, volatile unsigned long * addr)
  11. {
  12.     __asm__(
  13.         "btsl %1,%0"
  14.         :"+m" (ADDR)
  15.         :"Ir" (nr));
  16. }
  17. 在<Alternative.h>中
  18. /*
  19.  * Alternative inline assembly for SMP.
  20.  *
  21.  * The LOCK_PREFIX macro defined here replaces the LOCK and
  22.  * LOCK_PREFIX macros used everywhere in the source tree.
  23.  *
  24.  * SMP alternatives use the same data structures as the other
  25.  * alternatives and the X86_FEATURE_UP flag to indicate the case of a
  26.  * UP system running a SMP kernel.  The existing apply_alternatives()
  27.  * works fine for patching a SMP kernel for UP.
  28.  *
  29.  * The SMP alternative tables can be kept after boot and contain both
  30.  * UP and SMP versions of the instructions to allow switching back to
  31.  * SMP at runtime, when hotplugging in a new CPU, which is especially
  32.  * useful in virtualized environments.
  33.  *
  34.  * The very common lock prefix is handled as special case in a
  35.  * separate table which is a pure address list without replacement ptr
  36.  * and size information.  That keeps the table sizes small.
  37.  */
  38. #ifdef CONFIG_SMP
  39. #define LOCK_PREFIX /
  40.         ".section .smp_locks,/"a/"/n"   /
  41.         "  .align 4/n"          /
  42.         "  .long 661f/n" /* address */  /
  43.         ".previous/n"           /
  44.             "661:/n/tlock; "
  45. #else /* ! CONFIG_SMP */
  46. #define LOCK_PREFIX ""
  47. #endif

如果不需要原子性操作(可能你已经使用锁保护了数据,代码本身避免了竞争条件),非原子的位函数要比原子的位函数执行要快些。

 终于重新写完了T_T

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值