内核定义了atomic_t 数据类型,作为对整数计数器的原子操作的基础。 各个CPU平台有各自的原子操作实现方式,基本都是通过汇编实现的。

 原子操作
原子操作是Linux中提供的一种实现同步的方法,所谓原子操作是指某一操作在执行过程中是不可以被打断的,它要么全部执行完毕,要么就一点也不执行。也就是说原子操作是绝对不会出现该操作执行了一半,内核又去执行其他操作的情况。
原子操作分为原子位操作和原子整形操作。

a.整型原子操作
原子操作主要用于实现资源计数,很多引用计数(refcnt)就是通过原子操作实现的。原子类型定义如下:

typedef struct  {  volatile int counter;  }  atomic_t; 

volatile修饰字段告诉gcc不要对该类型的数据做优化处理,对它的访问都是对内存的访问,而不是对寄存器的访问。
原子操作API:

#define ATOMIC_INIT(i) { (i) }

功能:用于在定义原子变量时,初始化为指定的值。

用法:static atomic_t count = ATOMIC_INIT⑴;//定义atomic_t变量y并将它初始化为10。

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

功能:该函数读取v指向的原子变量的值。由于该操作本身就是原子的,只需要一次内存访问就能完成,因此定义为一个宏,并用C代码实现。

用法:atomic_read(&v);//原子地读取变量v的值

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

功能:设置v指向的原子变量的值为i。由于该操作本身就是原子的,只需要一次内存访问就能完成,因此定义为一个宏,并用C代码实现。

用法:atomic_set(&v,1);//原子地设置变量v的值为1

  1. void atomic_add(int i, atomic_t *v);


      该函数给原子类型的变量v增加值i。

    atomic_sub(int i, atomic_t *v);


      该函数从原子类型的变量v中减去i。

    int atomic_sub_and_test(int i, atomic_t *v);


      该函数从原子类型的变量v中减去i,并判断结果是否为0,如果为0,返回真,否则返回假。

    void atomic_inc(atomic_t *v);


      该函数对原子类型变量v原子地增加1。

    void atomic_dec(atomic_t *v);


      该函数对原子类型的变量v原子地减1。

    int atomic_dec_and_test(atomic_t *v);


      该函数对原子类型的变量v原子地减1,并判断结果是否为0,如果为0,返回真,否则返回假。

    int atomic_inc_and_test(atomic_t *v);


      该函数对原子类型的变量v原子地增加1,并判断结果是否为0,如果为0,返回真,否则返回假。

    int atomic_add_negative(int i, atomic_t *v);


      该函数对原子类型的变量v原子地增加I,并判断结果是否为负数,如果是,返回真,否则返回假。

    int atomic_add_return(int i, atomic_t *v);


      该函数对原子类型的变量v原子地增加i,并且返回指向v的指针。

    int atomic_sub_return(int i, atomic_t *v);


      该函数从原子类型的变量v中减去i,并且返回指向v的指针。

    int atomic_inc_return(atomic_t * v);


      该函数对原子类型的变量v原子地增加1并且返回指向v的指针。

    int atomic_dec_return(atomic_t * v);

      该函数对原子类型的变量v原子地减1并且返回指向v的指针。

      原子操作通常用于实现资源的引用计数,在TCP/IP协议栈的IP碎片处理中,就使用了引用计数,碎片队列结构struct ipq描述了一个IP碎片,字段refcnt就是引用计数器,它的类型为atomic_t,当创建IP碎片时(在函数ip_frag_create中),使用atomic_set函数把它设置为1,当引用该IP碎片时,就使用函数atomic_inc把引用计数加1。

      当不需要引用该IP碎片时,就使用函数ipq_put来释放该IP碎片,ipq_put使用函数atomic_dec_and_test把引用计数减1并判断引用计数是否为0,如果是就释放IP碎片。函数ipq_kill把IP碎片从ipq队列中删除,并把该删除的IP碎片的引用计数减1(通过使用函数atomic_dec实现)。

     
    b.位原子操作
    原子位操作API简介:
    void set_bit(int nr, void *addr)        原子设置addr所指的第nr位

    void clear_bit(int nr, void *addr)      原子的清空所指对象的第nr位

    void change_bit(nr, void *addr) 原子的翻转addr所指的第nr位

    int test_bit(nr, void *addr) 原子的返回addr位所指对象nr位

    int test_and_set_bit(nr, void *addr)    原子设置addr所指对象的第nr位,并返回原先的值

    int test_and_clear_bit(nr, void *addr)  原子清空addr所指对象的第nr位,并返回原先的值

    int test_and_change_bit(nr, void *addr) 原子翻转addr所指对象的第nr位,并返回原先的值

    如:标志寄存器EFLSGS的系统标志,用于控制I/O访问,可屏蔽硬件中断。共32位,不同的位代表不同的信息,
    对其中信息改变都是通过位操作实现的。

 以下原文:http://blog.csdn.net/qb_2008/article/details/6840808

在任何处理器平台下,都会有一些原子性操作,供操作系统使用,我们这里只讲x86下面的。在单处理器情况下,每条指令的执行都是原子性的,但在多处理器情况下,只有那些单独的读操作或写操作才是原子性的。为了弥补这一缺点,x86提供了附加的lock前缀,使带lock前缀的读修改写指令也能原子性执行。带lock前缀的指令在操作时会锁住总线,使自身的执行即使在多处理器间也是原子性执行的。xchg指令不带lock前缀也是原子性执行,也就是说xchg执行时默认会锁内存总线。原子性操作是线程间同步的基础,linux专门定义了一种只进行原子操作的类型atomic_t,并提供相关的原子读写调用API。本节就来分析这些原子操作在x86下的实现。

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

原子类型其实是int类型,只是禁止寄存器对其暂存。

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

原子类型的初始化。32位x86平台下atomic API在arch/x86/include/asm/atomic_32.h中实现。

  1. static inlineint atomic_read(const atomic_t *v) 
  2.     return v->counter; 
  3.  
  4. static inlinevoid atomic_set(atomic_t *v,int i) 
  5.     v->counter = i; 
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下都是原子性的。

  1. static inlinevoid atomic_add(int i, atomic_t *v) 
  2.     asm volatile(LOCK_PREFIX"addl %1,%0" 
  3.              : "+m" (v->counter) 
  4.              : "ir" (i)); 
  5.  
  6. static inlinevoid atomic_sub(int i, atomic_t *v) 
  7.     asm volatile(LOCK_PREFIX "subl %1,%0" 
  8.              : "+m" (v->counter) 
  9.              : "ir" (i)); 
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前缀。

  1. static inlineint atomic_sub_and_test(int i, atomic_t *v) 
  2.     unsigned char c; 
  3.  
  4.     asm volatile(LOCK_PREFIX"subl %2,%0; sete %1" 
  5.              : "+m" (v->counter), "=qm" (c) 
  6.              : "ir" (i) :"memory"); 
  7.     return c; 
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。

  1. static inlinevoid atomic_inc(atomic_t *v) 
  2.     asm volatile(LOCK_PREFIX"incl %0" 
  3.              : "+m" (v->counter)); 
  4.  
  5. static inlinevoid atomic_dec(atomic_t *v) 
  6.     asm volatile(LOCK_PREFIX"decl %0" 
  7.              : "+m" (v->counter)); 
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是递增递减操作。

  1. static inlineint atomic_dec_and_test(atomic_t *v) 
  2.     unsigned char c; 
  3.  
  4.     asm volatile(LOCK_PREFIX"decl %0; sete %1" 
  5.              : "+m" (v->counter), "=qm" (c) 
  6.              : : "memory"); 
  7.     return c != 0; 
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。

  1. static inlineint atomic_inc_and_test(atomic_t *v) 
  2.     unsigned char c; 
  3.  
  4.     asm volatile(LOCK_PREFIX"incl %0; sete %1" 
  5.              : "+m" (v->counter), "=qm" (c) 
  6.              : : "memory"); 
  7.     return c != 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。

  1. static inlineint atomic_add_negative(int i, atomic_t *v) 
  2.     unsigned char c; 
  3.  
  4.     asm volatile(LOCK_PREFIX"addl %2,%0; sets %1" 
  5.              : "+m" (v->counter), "=qm" (c) 
  6.              : "ir" (i) :"memory"); 
  7.     return c; 
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在加操作后检查结果是否为负数。

  1. static inlineint atomic_add_return(int i, atomic_t *v) 
  2.     int __i; 
  3. #ifdef CONFIG_M386 
  4.     unsigned long flags; 
  5.     if (unlikely(boot_cpu_data.x86 <= 3)) 
  6.         goto no_xadd; 
  7. #endif 
  8.     /* Modern 486+ processor */ 
  9.     __i = i; 
  10.     asm volatile(LOCK_PREFIX"xaddl %0, %1" 
  11.              : "+r" (i), "+m" (v->counter) 
  12.              : : "memory"); 
  13.     return i + __i; 
  14.  
  15. #ifdef CONFIG_M386 
  16. no_xadd: /* Legacy 386 processor */ 
  17.     local_irq_save(flags); 
  18.     __i = atomic_read(v); 
  19.     atomic_set(v, i + __i); 
  20.     local_irq_restore(flags); 
  21.     return i + __i; 
  22. #endif 
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
	/* Modern 486+ processor */
	__i = i;
	asm volatile(LOCK_PREFIX "xaddl %0, %1"
		     : "+r" (i), "+m" (v->counter)
		     : : "memory");
	return i + __i;

#ifdef CONFIG_M386
no_xadd: /* Legacy 386 processor */
	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这一指令实现的。

  1. static inlineint atomic_sub_return(int i, atomic_t *v) 
  2.     return atomic_add_return(-i, v); 
static inline int atomic_sub_return(int i, atomic_t *v)
{
	return atomic_add_return(-i, v);
}

atomic_sub_return 不仅执行减操作,而且把相减的结果返回。它是通过atomic_add_return实现的。

  1. static inlineint atomic_cmpxchg(atomic_t *v,int old,intnew
  2.     return cmpxchg(&v->counter, old,new); 
  3.  
  4. #define cmpxchg(ptr, o, n)                      \ 
  5.     ((__typeof__(*(ptr)))__cmpxchg((ptr), (unsigned long)(o),   \ 
  6.                        (unsigned long)(n),      \ 
  7.                        sizeof(*(ptr)))) 
  8.  
  9. static inline unsignedlong __cmpxchg(volatilevoid *ptr, unsigned long old, 
  10.                       unsigned longnew,int size) 
  11.     unsigned long prev; 
  12.     switch (size) { 
  13.     case 1: 
  14.         asm volatile(LOCK_PREFIX"cmpxchgb %b1,%2" 
  15.                  : "=a"(prev) 
  16.                  : "q"(new),"m"(*__xg(ptr)),"0"(old) 
  17.                  : "memory"); 
  18.         return prev; 
  19.     case 2: 
  20.         asm volatile(LOCK_PREFIX"cmpxchgw %w1,%2" 
  21.                  : "=a"(prev) 
  22.                  : "r"(new),"m"(*__xg(ptr)),"0"(old) 
  23.                  : "memory"); 
  24.         return prev; 
  25.     case 4: 
  26.         asm volatile(LOCK_PREFIX"cmpxchgl %k1,%2" 
  27.                  : "=a"(prev) 
  28.                  : "r"(new),"m"(*__xg(ptr)),"0"(old) 
  29.                  : "memory"); 
  30.         return prev; 
  31.     case 8: 
  32.         asm volatile(LOCK_PREFIX"cmpxchgq %1,%2" 
  33.                  : "=a"(prev) 
  34.                  : "r"(new),"m"(*__xg(ptr)),"0"(old) 
  35.                  : "memory"); 
  36.         return prev; 
  37.     } 
  38.     return old; 
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类型变量中原有的值。

  1. static inlineint atomic_xchg(atomic_t *v,intnew
  2.     return xchg(&v->counter,new); 
  3.  
  4. #define xchg(ptr, v)                            \ 
  5.     ((__typeof__(*(ptr)))__xchg((unsigned long)(v), (ptr), sizeof(*(ptr)))) 
  6.  
  7. static inline unsignedlong __xchg(unsignedlong x,volatilevoid *ptr, 
  8.                    int size) 
  9.     switch (size) { 
  10.     case 1: 
  11.         asm volatile("xchgb %b0,%1" 
  12.                  : "=q" (x) 
  13.                  : "m" (*__xg(ptr)), "0" (x) 
  14.                  : "memory"); 
  15.         break
  16.     case 2: 
  17.         asm volatile("xchgw %w0,%1" 
  18.                  : "=r" (x) 
  19.                  : "m" (*__xg(ptr)), "0" (x) 
  20.                  : "memory"); 
  21.         break
  22.     case 4: 
  23.         asm volatile("xchgl %k0,%1" 
  24.                  : "=r" (x) 
  25.                  : "m" (*__xg(ptr)), "0" (x) 
  26.                  : "memory"); 
  27.         break
  28.     case 8: 
  29.         asm volatile("xchgq %0,%1" 
  30.                  : "=r" (x) 
  31.                  : "m" (*__xg(ptr)), "0" (x) 
  32.                  : "memory"); 
  33.         break
  34.     } 
  35.     return x; 
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指令实现。

  1. /**
  2. * atomic_add_unless - add unless the number is already a given value
  3. * @v: pointer of type atomic_t
  4. * @a: the amount to add to v...
  5. * @u: ...unless v is equal to u.
  6. *
  7. * Atomically adds @a to @v, so long as @v was not already @u.
  8. * Returns non-zero if @v was not @u, and zero otherwise.
  9. */ 
  10. static inlineint atomic_add_unless(atomic_t *v,int a,int u) 
  11.     int c, old; 
  12.     c = atomic_read(v); 
  13.     for (;;) { 
  14.         if (unlikely(c == (u))) 
  15.             break
  16.         old = atomic_cmpxchg((v), c, c + (a)); 
  17.         if (likely(old == c)) 
  18.             break
  19.         c = old; 
  20.     } 
  21.     return c != (u); 
/**
 * atomic_add_unless - add unless the number is already a given value
 * @v: pointer of type atomic_t
 * @a: the amount to add to v...
 * @u: ...unless v is equal to u.
 *
 * Atomically adds @a to @v, so long as @v was not already @u.
 * Returns non-zero if @v was not @u, and zero otherwise.
 */
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中间可能有其它的写操作,所以要循环检查自己的值是否被写进去。

  1. #define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0) 
  2.  
  3. #define atomic_inc_return(v)  (atomic_add_return(1, v)) 
  4. #define atomic_dec_return(v)  (atomic_sub_return(1, v)) 
#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,并返回相减结果。

  1. #define atomic_clear_mask(mask, addr)               \ 
  2.     asm volatile(LOCK_PREFIX "andl %0,%1"           \ 
  3.              : : "r" (~(mask)),"m" (*(addr)) :"memory"
#define atomic_clear_mask(mask, addr)				\
	asm volatile(LOCK_PREFIX "andl %0,%1"			\
		     : : "r" (~(mask)), "m" (*(addr)) : "memory")

atomic_clear_mask清除变量某些位。

  1. #define atomic_set_mask(mask, addr)             \ 
  2.     asm volatile(LOCK_PREFIX "orl %0,%1"                \ 
  3.              : : "r" (mask),"m" (*(addr)) :"memory"
#define atomic_set_mask(mask, addr)				\
	asm volatile(LOCK_PREFIX "orl %0,%1"				\
		     : : "r" (mask), "m" (*(addr)) : "memory")


atomic_set_mask将变量的某些位置位。

  1. /* Atomic operations are already serializing on x86 */ 
  2. #define smp_mb__before_atomic_dec() barrier() 
  3. #define smp_mb__after_atomic_dec()  barrier() 
  4. #define smp_mb__before_atomic_inc() barrier() 
  5. #define smp_mb__after_atomic_inc()  barrier() 
/* Atomic operations are already serializing on x86 */
#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类型代替临界区操作,也可以加快不少速度。

GitHub 加速计划 / li / linux-dash
10.39 K
1.2 K
下载
A beautiful web dashboard for Linux
最近提交(Master分支:2 个月前 )
186a802e added ecosystem file for PM2 4 年前
5def40a3 Add host customization support for the NodeJS version 4 年前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐