本文转载自:点击打开链接
在任何处理器平台下,都会有一些原子性操作,供操作系统使用,我们这里只讲x86下面的。
原子操作的概念来自物理学中微粒的概念原子不可再分性,说明原子操作是不会被线程调度机制打断的操作,不会被编译器自动优化掉,必定执行的操作;
在单处理器情况下,每条指令的执行都是原子性的,但在多处理器情况下,只有那些单独的读操作或写操作才是原子性的。为了弥补这一缺点,x86提供了附加的lock前缀,使带lock前缀的读修改写指令也能原子性执行。带lock前缀的指令在操作时会锁住总线,使自身的执行即使在多处理器间也是原子性执行的。xchg指令不带lock前缀也是原子性执行,也就是说xchg执行时默认会锁内存总线。原子性操作是线程间同步的基础,Linux专门定义了一种只进行原子操作的类型atomic_t,并提供相关的原子读写调用API。本节就来分析这些原子操作在x86下的实现。
- typedef struct {
- volatile int counter;
- } atomic_t;
原子类型其实是int类型,只是禁止寄存器对其暂存。
- #define ATOMIC_INIT(i) { (i) }
原子类型的初始化。32位x86平台下atomic API在arch/x86/include/asm/atomic_32.h中实现。
- static inline int atomic_read(const atomic_t *v)
- {
- return v->counter;
- }
-
- static inline void atomic_set(atomic_t *v, int i)
- {
- v->counter = i;
- }
单独的读操作或者写操作,在x86下都是原子性的。
- static inline void atomic_add(int i, atomic_t *v)
- {
- asm volatile(LOCK_PREFIX "addl %1,%0"
- : "+m" (v->counter)
- : "ir" (i));
- }
-
- static inline void atomic_sub(int i, atomic_t *v)
- {
- asm volatile(LOCK_PREFIX "subl %1,%0"
- : "+m" (v->counter)
- : "ir" (i));
- }
atomic_add和atomic_sub属于读修改写操作,实现时需要加lock前缀。
- static inline int atomic_sub_and_test(int i, atomic_t *v)
- {
- unsigned char c;
-
- asm volatile(LOCK_PREFIX "subl %2,%0; sete %1"
- : "+m" (v->counter), "=qm" (c)
- : "ir" (i) : "memory");
- return c;
- }
atomic_sub_and_test执行完减操作后检查结果是否为0。
- static inline void atomic_inc(atomic_t *v)
- {
- asm volatile(LOCK_PREFIX "incl %0"
- : "+m" (v->counter));
- }
-
- static inline void atomic_dec(atomic_t *v)
- {
- asm volatile(LOCK_PREFIX "decl %0"
- : "+m" (v->counter));
- }
atomic_inc和atomic_dec是递增递减操作。
- static inline int atomic_dec_and_test(atomic_t *v)
- {
- unsigned char c;
-
- asm volatile(LOCK_PREFIX "decl %0; sete %1"
- : "+m" (v->counter), "=qm" (c)
- : : "memory");
- return c != 0;
- }
atomic_dec_and_test在递减后检查结果是否为0。
- static inline int atomic_inc_and_test(atomic_t *v)
- {
- unsigned char c;
-
- asm volatile(LOCK_PREFIX "incl %0; sete %1"
- : "+m" (v->counter), "=qm" (c)
- : : "memory");
- return c != 0;
- }
atomic_inc_and_test在递增后检查结果是否为0。
- static inline int atomic_add_negative(int i, atomic_t *v)
- {
- unsigned char c;
-
- asm volatile(LOCK_PREFIX "addl %2,%0; sets %1"
- : "+m" (v->counter), "=qm" (c)
- : "ir" (i) : "memory");
- return c;
- }
atomic_add_negative在加操作后检查结果是否为负数。
- static inline int atomic_add_return(int i, atomic_t *v)
- {
- int __i;
- #ifdef CONFIG_M386
- unsigned long flags;
- if (unlikely(boot_cpu_data.x86 <= 3))
- goto no_xadd;
- #endif
-
- __i = i;
- asm volatile(LOCK_PREFIX "xaddl %0, %1"
- : "+r" (i), "+m" (v->counter)
- : : "memory");
- return i + __i;
-
- #ifdef CONFIG_M386
- no_xadd:
- local_irq_save(flags);
- __i = atomic_read(v);
- atomic_set(v, i + __i);
- local_irq_restore(flags);
- return i + __i;
- #endif
- }
atomic_add_return 不仅执行加操作,而且把相加的结果返回。它是通过xadd这一指令实现的。
- static inline int atomic_sub_return(int i, atomic_t *v)
- {
- return atomic_add_return(-i, v);
- }
atomic_sub_return 不仅执行减操作,而且把相减的结果返回。它是通过atomic_add_return实现的。
- static inline int atomic_cmpxchg(atomic_t *v, int old, int new)
- {
- return cmpxchg(&v->counter, old, new);
- }
-
- #define cmpxchg(ptr, o, n) \
- ((__typeof__(*(ptr)))__cmpxchg((ptr), (unsigned long)(o), \
- (unsigned long)(n), \
- sizeof(*(ptr))))
-
- static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old,
- unsigned long new, int size)
- {
- unsigned long prev;
- switch (size) {
- case 1:
- asm volatile(LOCK_PREFIX "cmpxchgb %b1,%2"
- : "=a"(prev)
- : "q"(new), "m"(*__xg(ptr)), "0"(old)
- : "memory");
- return prev;
- case 2:
- asm volatile(LOCK_PREFIX "cmpxchgw %w1,%2"
- : "=a"(prev)
- : "r"(new), "m"(*__xg(ptr)), "0"(old)
- : "memory");
- return prev;
- case 4:
- asm volatile(LOCK_PREFIX "cmpxchgl %k1,%2"
- : "=a"(prev)
- : "r"(new), "m"(*__xg(ptr)), "0"(old)
- : "memory");
- return prev;
- case 8:
- asm volatile(LOCK_PREFIX "cmpxchgq %1,%2"
- : "=a"(prev)
- : "r"(new), "m"(*__xg(ptr)), "0"(old)
- : "memory");
- return prev;
- }
- return old;
- }
atomic_cmpxchg是由cmpxchg指令完成的。它把旧值同atomic_t类型的值相比较,如果相同,就把新值存入atomic_t类型的值中,返回atomic_t类型变量中原有的值。
- static inline int atomic_xchg(atomic_t *v, int new)
- {
- return xchg(&v->counter, new);
- }
-
- #define xchg(ptr, v) \
- ((__typeof__(*(ptr)))__xchg((unsigned long)(v), (ptr), sizeof(*(ptr))))
-
- static inline unsigned long __xchg(unsigned long x, volatile void *ptr,
- int size)
- {
- switch (size) {
- case 1:
- asm volatile("xchgb %b0,%1"
- : "=q" (x)
- : "m" (*__xg(ptr)), "0" (x)
- : "memory");
- break;
- case 2:
- asm volatile("xchgw %w0,%1"
- : "=r" (x)
- : "m" (*__xg(ptr)), "0" (x)
- : "memory");
- break;
- case 4:
- asm volatile("xchgl %k0,%1"
- : "=r" (x)
- : "m" (*__xg(ptr)), "0" (x)
- : "memory");
- break;
- case 8:
- asm volatile("xchgq %0,%1"
- : "=r" (x)
- : "m" (*__xg(ptr)), "0" (x)
- : "memory");
- break;
- }
- return x;
- }
atomic_xchg则是将新值存入atomic_t类型的变量,并将变量的旧值返回。它使用xchg指令实现。
-
-
-
-
-
-
-
-
-
- static inline int atomic_add_unless(atomic_t *v, int a, int u)
- {
- int c, old;
- c = atomic_read(v);
- for (;;) {
- if (unlikely(c == (u)))
- break;
- old = atomic_cmpxchg((v), c, c + (a));
- if (likely(old == c))
- break;
- c = old;
- }
- return c != (u);
- }
atomic_add_unless的功能比较特殊。它检查v是否等于u,如果不是则把v的值加上a,返回值表示相加前v是否等于u。因为在atomic_read和atomic_cmpxchg中间可能有其它的写操作,所以要循环检查自己的值是否被写进去。
- #define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0)
-
- #define atomic_inc_return(v) (atomic_add_return(1, v))
- #define atomic_dec_return(v) (atomic_sub_return(1, v))
atomic_inc_not_zero在v值不是0时加1。
atomic_inc_return对v值加1,并返回相加结果。
atomic_dec_return对v值减1,并返回相减结果。
- #define atomic_clear_mask(mask, addr) \
- asm volatile(LOCK_PREFIX "andl %0,%1" \
- : : "r" (~(mask)), "m" (*(addr)) : "memory")
atomic_clear_mask清除变量某些位。
- #define atomic_set_mask(mask, addr) \
- asm volatile(LOCK_PREFIX "orl %0,%1" \
- : : "r" (mask), "m" (*(addr)) : "memory")
atomic_set_mask将变量的某些位置位。
-
- #define smp_mb__before_atomic_dec() barrier()
- #define smp_mb__after_atomic_dec() barrier()
- #define smp_mb__before_atomic_inc() barrier()
- #define smp_mb__after_atomic_inc() barrier()
因为x86的atomic操作大多使用原子指令或者带lock前缀的指令。带lock前缀的指令执行前会完成之前的读写操作,对于原子操作来说不会受之前对同一位置的读写操作,所以这里只是用空操作barrier()代替。barrier()的作用相当于告诉编译器这里有一个内存屏障,放弃在寄存器中的暂存值,重新从内存中读入。
本节的atomic_t类型操作是最基础的,为了介绍下面的内容,必须先介绍它。如果可以使用atomic_t类型代替临界区操作,也可以加快不少速度。