1. 程式人生 > >核心的同步機制(原子鎖)

核心的同步機制(原子鎖)

轉自https://blog.csdn.net/fenglifeng1987/article/details/8172975

 

摘自《linux裝置驅動開發詳解》第七章

1.設定原子變數的值
           void atomic_set(atomic_t *v, int i); //設定原子變數的值為i
           atomic_t v = ATOMIC_INIT(0); //定義原子變數v並初始化為0
2.獲取原子變數的值
            atomic_read(atomic_t *v); //返回原子變數的值
3.原子變數加/減
           void atomic_add(int i, atomic_t *v); //原子變數增加i
           void atomic_sub(int i, atomic_t *v); //原子變數減少i
4.原子變數自增/自減
          void atomic_inc(atomic_t *v); //原子變數增加1
          void atomic_dec(atomic_t *v); //原子變數減少1

5.操作並測試
            int atomic_inc_and_test(atomic_t *v);
            int atomic_dec_and_test(atomic_t *v);
            int atomic_sub_and_test(int i, atomic_t *v);
上述操作對原子變數執行自增、自減和減操作後(注意沒有加)測試其是否為0,
為0 則返回true,否則返回false。
6.操作並返回
           int atomic_add_return(int i, atomic_t *v);
           int atomic_sub_return(int i, atomic_t *v);
           int atomic_inc_return(atomic_t *v);
           int atomic_dec_return(atomic_t *v);
上述操作對原子變數進行加/減和自增/自減操作,並返回新的值。

       atomic。在atomic.h中有著它的定義以及操作函式。

 
  1. typedef struct { volatile int counter; } atomic_t;

  2.  
  3. #define ATOMIC_INIT(i) { (i) }

  4.  
  5. /*

  6. * atomic_read - read atomic variable

  7. * @v: pointer of type atomic_t

  8. *

  9. * Atomically reads the value of @v.

  10. */

  11. #define atomic_read(v) ((v)->counter)

  12.  
  13. /*

  14. * atomic_set - set atomic variable

  15. * @v: pointer of type atomic_t

  16. * @i: required value

  17. *

  18. * Atomically sets the value of @v to @i.

  19. */

  20. #define atomic_set(v,i) ((v)->counter = (i))

  21.  
  22. /*

  23. * atomic_add - add integer to atomic variable

  24. * @i: integer value to add

  25. * @v: pointer of type atomic_t

  26. *

  27. * Atomically adds @i to @v.

  28. */

  29. static __inline__ void atomic_add(int i, atomic_t * v)

  30. {

  31. if (cpu_has_llsc && R10000_LLSC_WAR) {

  32. unsigned long temp;

  33.  
  34. __asm__ __volatile__(

  35. " .set mips3 \n"

  36. "1: ll %0, %1 # atomic_add \n"

  37. " addu %0, %2 \n"

  38. " sc %0, %1 \n"

  39. " beqzl %0, 1b \n"

  40. " .set mips0 \n"

  41. : "=&r" (temp), "=m" (v->counter)

  42. : "Ir" (i), "m" (v->counter));

  43. } else if (cpu_has_llsc) {

  44. unsigned long temp;

  45.  
  46. __asm__ __volatile__(

  47. " .set mips3 \n"

  48. "1: ll %0, %1 # atomic_add \n"

  49. " addu %0, %2 \n"

  50. " sc %0, %1 \n"

  51. " beqz %0, 2f \n"

  52. " .subsection 2 \n"

  53. "2: b 1b \n"

  54. " .previous \n"

  55. " .set mips0 \n"

  56. : "=&r" (temp), "=m" (v->counter)

  57. : "Ir" (i), "m" (v->counter));

  58. } else {

  59. unsigned long flags;

  60.  
  61. raw_local_irq_save(flags);

  62. v->counter += i;

  63. raw_local_irq_restore(flags);

  64. }

  65. }

  66.  
  67. /*

  68. * atomic_sub - subtract the atomic variable

  69. * @i: integer value to subtract

  70. * @v: pointer of type atomic_t

  71. *

  72. * Atomically subtracts @i from @v.

  73. */

  74. static __inline__ void atomic_sub(int i, atomic_t * v)

  75. {

  76. if (cpu_has_llsc && R10000_LLSC_WAR) {

  77. unsigned long temp;

  78.  
  79. __asm__ __volatile__(

  80. " .set mips3 \n"

  81. "1: ll %0, %1 # atomic_sub \n"

  82. " subu %0, %2 \n"

  83. " sc %0, %1 \n"

  84. " beqzl %0, 1b \n"

  85. " .set mips0 \n"

  86. : "=&r" (temp), "=m" (v->counter)

  87. : "Ir" (i), "m" (v->counter));

  88. } else if (cpu_has_llsc) {

  89. unsigned long temp;

  90.  
  91. __asm__ __volatile__(

  92. " .set mips3 \n"

  93. "1: ll %0, %1 # atomic_sub \n"

  94. " subu %0, %2 \n"

  95. " sc %0, %1 \n"

  96. " beqz %0, 2f \n"

  97. " .subsection 2 \n"

  98. "2: b 1b \n"

  99. " .previous \n"

  100. " .set mips0 \n"

  101. : "=&r" (temp), "=m" (v->counter)

  102. : "Ir" (i), "m" (v->counter));

  103. } else {

  104. unsigned long flags;

  105.  
  106. raw_local_irq_save(flags);

  107. v->counter -= i;

  108. raw_local_irq_restore(flags);

  109. }

  110. }

 
  1. /*

  2.  * atomic_sub_and_test - subtract value from variable and test result

  3.  * @i: integer value to subtract

  4.  * @v: pointer of type atomic_t

  5.  *

  6.  * Atomically subtracts @i from @v and returns

  7.  * true if the result is zero, or false for all

  8.  * other cases.

  9.  */

  10. #define atomic_sub_and_test(i,v) (atomic_sub_return((i), (v)) == 0)

  11.  
  12.  
  13. /*

  14.  * atomic_inc_and_test - increment and test

  15.  * @v: pointer of type atomic_t

  16.  *

  17.  * Atomically increments @v by 1

  18.  * and returns true if the result is zero, or false for all

  19.  * other cases.

  20.  */

  21. #define atomic_inc_and_test(v) (atomic_inc_return(v) == 0)

  22.  
  23.  
  24. /*

  25.  * atomic_dec_and_test - decrement by 1 and test

  26.  * @v: pointer of type atomic_t

  27.  *

  28.  * Atomically decrements @v by 1 and

  29.  * returns true if the result is 0, or false for all other

  30.  * cases.

  31.  */

  32. #define atomic_dec_and_test(v) (atomic_sub_return(1, (v)) == 0)

  33.  

其實就是設定一個整形變數,對這個整形變數進行加減操作。可應用於對引用資源的計數。

 

        在對這個整形變數進行加減的過程中,是在原子狀態下進行的,組合語言先不管,那幾段少的可憐的c程式碼中,v->count進行加減的時候都是要關中斷的。確保當前process佔用核心。

       原子鎖也可以用於同步。比如下面的程式,至允許在一個程序中開啟資源。程式也是摘自《linux裝置驅動詳解》

  1. static atomic_t xxx_available = ATOMIC_INIT(1); /*定義原子變數*/

  2.  
  3. static int xxx_open(struct inode *inode, struct file *filp)

  4. {

  5. ...

  6. if (!atomic_dec_and_test(&xxx_available)) //如果xxx_available=0,返回1

  7. {

  8. atomic_inc(&xxx_available);

  9. return - EBUSY; /*已經開啟*/

  10. }

  11. ...

  12. return 0; /* 成功*/

  13. }

  14.  
  15. static int xxx_release(struct inode *inode, struct file *filp)

  16. {

  17. atomic_inc(&xxx_available); /* 釋放裝置*/

  18. return 0;

  19. }

 

還有位操作的的原子鎖,原理跟上面的差不多,只不過這是用bit操作,這樣操作更簡潔明瞭,非0即1,就是缺少了計數這個功能。

1.設定位
          void set_bit(nr, void *addr);
         上述操作設定addr 地址的第nr 位,所謂設定位即將位寫為1。
2.清除位
         void clear_bit(nr, void *addr);
       上述操作清除addr 地址的第nr 位,所謂清除位即將位寫為0。
3.改變位
         void change_bit(nr, void *addr);
        上述操作對addr 地址的第nr 位進行反置。
4.測試位
         test_bit(nr, void *addr);
        上述操作返回addr 地址的第nr 位。

5.測試並操作位
         int test_and_set_bit(nr, void *addr);
         int test_and_clear_bit(nr, void *addr);
         int test_and_change_bit(nr, void *addr);
上述test_and_xxx_bit(nr, void *addr)操作等同於執行test_bit (nr, void *addr)後
再執行xxx_bit(nr, void *addr)。