原子操作可以保证指令以原子的方式执行,执行过程不被打断。
内核提供了两组原子操作接口:一组针对整数进行操作,另一组针对单独的位进行操作。大多数体系结构要么本来就支持简单的原子操作,要么就为单步执行提供了锁内存总线的指令。
- 原子整数操作
针对整数的原子操作只能对atomic_t类型的数据进行处理。引入该类型的原因:
首先,让原子函数只接收atomic_t类型的操作数,可以确保原子操作只与这种特殊类型数据一起使用。保证了该类型数据不会被传递给其他任何非原子函数。
其次,使用atomic_t类型确保编译器不对相应的值进行访问优化,这使得原子操作最终接收到正确的内存地址而不是一个别名。
最后,在不同的体系结构上实现原子操作的时候,使用atomic_t类型可以屏蔽其间的差异。例如:
- 在《Atomic.h(asm-i386)》中
- /*
- * Make sure gcc doesn't try to be clever and move things around
- * on us. We need to use _exactly_ the address the user gave us,
- * not some alias that contains the same information.
- */
- typedef struct { int counter; } atomic_t;
- 在《Atomic.h(asm-sparc)》中
- typedef struct { volatile int counter; } atomic_t;
- /* This is the old 24-bit implementation. It's still used internally
- * by some sparc-specific code, notably the semaphore implementation.
- */
- typedef struct { volatile int counter; } atomic24_t;
the old 24-bit implementation:
在SPARC体系结构上,原子操作的实现不同于其他体系结构:32位int类型的低8位嵌入一个锁,因为SPARC体系结构对原子操作缺乏指令级的支持,所以只能利用该锁来避免对原子类型数据的并发访问。
- 在<Atomic.h(asm-sparc)>中
- /* This is the old 24-bit implementation. It's still used internally
- * by some sparc-specific code, notably the semaphore implementation.
- */
- typedef struct { volatile int counter; } atomic24_t;
- #ifndef CONFIG_SMP
- #define ATOMIC24_INIT(i) { (i) }
- #define atomic24_read(v) ((v)->counter)
- #define atomic24_set(v, i) (((v)->counter) = i)
- #else
- /* We do the bulk of the actual work out of line in two common
- * routines in assembler, see arch/sparc/lib/atomic.S for the
- * "fun" details.
- *
- * For SMP the trick is you embed the spin lock byte within
- * the word, use the low byte so signedness is easily retained
- * via a quick arithmetic shift. It looks like this:
- *
- * ----------------------------------------
- * | signed 24-bit counter value | lock | atomic_t
- * ----------------------------------------
- * 31 8 7 0
- */
- #define ATOMIC24_INIT(i) { ((i) << 8) }
相关操作:
知道什么叫欲哭无泪吗?我现在就是。早上写了上面的部分并保存,下午继续把这篇笔记写完了。但是可恶的CSDN不知道为啥只能发表上面的部分,下午写的都没有了!!我现在是一肚子悔恨啊,我已经不是第一次遇到这样的情况了,但是我居然只是拷贝没有把写好的粘贴到word里,后来有拷贝了其他东西到缓冲区就把写好的东西清空了,呜呜,现在不得不再重新写一次!!我的肠子现在都悔青了!!!
- 在<Atomic.h(include/asm-i386)>
- /*
- * Atomic operations that C can't guarantee us. Useful for
- * resource counting etc..
- */
- /*
- * Make sure gcc doesn't try to be clever and move things around
- * on us. We need to use _exactly_ the address the user gave us,
- * not some alias that contains the same information.
- */
- typedef struct { int counter; } atomic_t;
- #define ATOMIC_INIT(i) { (i) }
- /**
- * atomic_read - read atomic variable
- * @v: pointer of type atomic_t
- *
- * Atomically reads the value of @v.
- */
- #define atomic_read(v) ((v)->counter)
- /**
- * atomic_set - set atomic variable
- * @v: pointer of type atomic_t
- * @i: required value
- *
- * Atomically sets the value of @v to @i.
- */
- #define atomic_set(v,i) (((v)->counter) = (i))
- /**
- * atomic_add - add integer to atomic variable
- * @i: integer value to add
- * @v: pointer of type atomic_t
- *
- * Atomically adds @i to @v.
- */
- static __inline__ void atomic_add(int i, atomic_t *v)
- {
- __asm__ __volatile__(
- LOCK_PREFIX "addl %1,%0"
- :"+m" (v->counter)
- :"ir" (i));
- }
- /**
- * atomic_sub - subtract the atomic variable
- * @i: integer value to subtract
- * @v: pointer of type atomic_t
- *
- * Atomically subtracts @i from @v.
- */
- static __inline__ void atomic_sub(int i, atomic_t *v)
- {
- __asm__ __volatile__(
- LOCK_PREFIX "subl %1,%0"
- :"+m" (v->counter)
- :"ir" (i));
- }
- /**
- * atomic_sub_and_test - subtract value from variable and test result
- * @i: integer value to subtract
- * @v: pointer of type atomic_t
- *
- * Atomically subtracts @i from @v and returns
- * true if the result is zero, or false for all
- * other cases.
- */
- 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_inc - increment atomic variable
- * @v: pointer of type atomic_t
- *
- * Atomically increments @v by 1.
- */
- static __inline__ void atomic_inc(atomic_t *v)
- {
- __asm__ __volatile__(
- LOCK_PREFIX "incl %0"
- :"+m" (v->counter));
- }
- /**
- * atomic_dec - decrement atomic variable
- * @v: pointer of type atomic_t
- *
- * Atomically decrements @v by 1.
- */
- static __inline__ void atomic_dec(atomic_t *v)
- {
- __asm__ __volatile__(
- LOCK_PREFIX "decl %0"
- :"+m" (v->counter));
- }
- /**
- * atomic_dec_and_test - decrement and test
- * @v: pointer of type atomic_t
- *
- * Atomically decrements @v by 1 and
- * returns true if the result is 0, or false for all other
- * cases.
- */
- 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_inc_and_test - increment and test
- * @v: pointer of type atomic_t
- *
- * Atomically increments @v by 1
- * and returns true if the result is zero, or false for all
- * other cases.
- */
- 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_add_negative - add and test if negative
- * @v: pointer of type atomic_t
- * @i: integer value to add
- *
- * Atomically adds @i to @v and returns true
- * if the result is negative, or false when
- * result is greater than or equal to zero.
- */
- 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_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,这里没有特殊的数据类型。只要指针指向了任何数据,就可以对它进行操作。
相关操作:
- 在<Bitops.h(include/i386)>中
- /*
- * These have to be done with inline assembly: that way the bit-setting
- * is guaranteed to be atomic. All bit operations return 0 if the bit
- * was cleared before the operation and != 0 if it was not.
- *
- * bit 0 is the LSB of addr; bit 32 is the LSB of (addr+1).
- */
- #define ADDR (*(volatile long *) addr)
- /**
- * set_bit - Atomically set a bit in memory
- * @nr: the bit to set
- * @addr: the address to start counting from
- *
- * This function is atomic and may not be reordered. See __set_bit()
- * if you do not require the atomic guarantees.
- *
- * Note: there are no guarantees that this function will not be reordered
- * on non x86 architectures, so if you are writting portable code,
- * make sure not to rely on its reordering guarantees.
- *
- * Note that @nr may be almost arbitrarily large; this function is not
- * restricted to acting on a single-word quantity.
- */
- static inline void set_bit(int nr, volatile unsigned long * addr)
- {
- __asm__ __volatile__( LOCK_PREFIX
- "btsl %1,%0"
- :"+m" (ADDR)
- :"Ir" (nr));
- }
- /**
- * clear_bit - Clears a bit in memory
- * @nr: Bit to clear
- * @addr: Address to start counting from
- *
- * clear_bit() is atomic and may not be reordered. However, it does
- * not contain a memory barrier, so if it is used for locking purposes,
- * you should call smp_mb__before_clear_bit() and/or smp_mb__after_clear_bit()
- * in order to ensure changes are visible on other processors.
- */
- static inline void clear_bit(int nr, volatile unsigned long * addr)
- {
- __asm__ __volatile__( LOCK_PREFIX
- "btrl %1,%0"
- :"+m" (ADDR)
- :"Ir" (nr));
- }
- /**
- * change_bit - Toggle a bit in memory
- * @nr: Bit to change
- * @addr: Address to start counting from
- *
- * change_bit() is atomic and may not be reordered. It may be
- * reordered on other architectures than x86.
- * Note that @nr may be almost arbitrarily large; this function is not
- * restricted to acting on a single-word quantity.
- */
- static inline void change_bit(int nr, volatile unsigned long * addr)
- {
- __asm__ __volatile__( LOCK_PREFIX
- "btcl %1,%0"
- :"+m" (ADDR)
- :"Ir" (nr));
- }
- /**
- * test_and_set_bit - Set a bit and return its old value
- * @nr: Bit to set
- * @addr: Address to count from
- *
- * This operation is atomic and cannot be reordered.
- * It may be reordered on other architectures than x86.
- * It also implies a memory barrier.
- */
- static inline int test_and_set_bit(int nr, volatile unsigned long * addr)
- {
- int oldbit;
- __asm__ __volatile__( LOCK_PREFIX
- "btsl %2,%1/n/tsbbl %0,%0"
- :"=r" (oldbit),"+m" (ADDR)
- :"Ir" (nr) : "memory");
- return oldbit;
- }
- /**
- * test_and_clear_bit - Clear a bit and return its old value
- * @nr: Bit to clear
- * @addr: Address to count from
- *
- * This operation is atomic and cannot be reordered.
- * It can be reorderdered on other architectures other than x86.
- * It also implies a memory barrier.
- */
- static inline int test_and_clear_bit(int nr, volatile unsigned long * addr)
- {
- int oldbit;
- __asm__ __volatile__( LOCK_PREFIX
- "btrl %2,%1/n/tsbbl %0,%0"
- :"=r" (oldbit),"+m" (ADDR)
- :"Ir" (nr) : "memory");
- return oldbit;
- }
- /**
- * test_and_change_bit - Change a bit and return its old value
- * @nr: Bit to change
- * @addr: Address to count from
- *
- * This operation is atomic and cannot be reordered.
- * It also implies a memory barrier.
- */
- static inline int test_and_change_bit(int nr, volatile unsigned long* addr)
- {
- int oldbit;
- __asm__ __volatile__( LOCK_PREFIX
- "btcl %2,%1/n/tsbbl %0,%0"
- :"=r" (oldbit),"+m" (ADDR)
- :"Ir" (nr) : "memory");
- return oldbit;
- }
为了方便,内核还提供了一组与上述操作相对应的非原子位函数。非原子位函数与原子位函数的操作完全相同,但是前者不保证原子性,代码里没有LOCK_PREFIX,其名字前缀多了两个下划线:
- /**
- * __set_bit - Set a bit in memory
- * @nr: the bit to set
- * @addr: the address to start counting from
- *
- * Unlike set_bit(), this function is non-atomic and may be reordered.
- * If it's called on the same region of memory simultaneously, the effect
- * may be that only one operation succeeds.
- */
- static inline void __set_bit(int nr, volatile unsigned long * addr)
- {
- __asm__(
- "btsl %1,%0"
- :"+m" (ADDR)
- :"Ir" (nr));
- }
- 在<Alternative.h>中
- /*
- * Alternative inline assembly for SMP.
- *
- * The LOCK_PREFIX macro defined here replaces the LOCK and
- * LOCK_PREFIX macros used everywhere in the source tree.
- *
- * SMP alternatives use the same data structures as the other
- * alternatives and the X86_FEATURE_UP flag to indicate the case of a
- * UP system running a SMP kernel. The existing apply_alternatives()
- * works fine for patching a SMP kernel for UP.
- *
- * The SMP alternative tables can be kept after boot and contain both
- * UP and SMP versions of the instructions to allow switching back to
- * SMP at runtime, when hotplugging in a new CPU, which is especially
- * useful in virtualized environments.
- *
- * The very common lock prefix is handled as special case in a
- * separate table which is a pure address list without replacement ptr
- * and size information. That keeps the table sizes small.
- */
- #ifdef CONFIG_SMP
- #define LOCK_PREFIX /
- ".section .smp_locks,/"a/"/n" /
- " .align 4/n" /
- " .long 661f/n" /* address */ /
- ".previous/n" /
- "661:/n/tlock; "
- #else /* ! CONFIG_SMP */
- #define LOCK_PREFIX ""
- #endif
如果不需要原子性操作(可能你已经使用锁保护了数据,代码本身避免了竞争条件),非原子的位函数要比原子的位函数执行要快些。
终于重新写完了T_T