内核同步方法主要有:原子操作、自旋锁、读写自旋锁、信号量、读写信号、完成变量等。内核提供了两组原子操作接口:一是对整数进行操作;而是对位进行操作。在linux支持的所有体系接口上都实现了这两组接口:要么支持简单的原子操作;要么为单步执行提供锁内存总线的指令。
在include\linux\types.h中对原子变量atomic_t进行了定义:
- typedef struct {
- int counter;
- } atomic_t;
- atomic_t V;
- atomic_t V1=ATOMIC_INIT(0);
- atomic_set(&V,4);
- atomic_add(2,&V);
- atomic_inc(&V);
原子操作通常是内联函数,通过内嵌汇编来实现。原子性确保执行在执行期间不被打断,要么执行完,要么不执行。顺序性确保即使两条或多条指令出现在独立的线程或者处理器中,但他们本该的执行顺序依然要保持。顺序可以通过屏障(barrier)来实施。
原子操作接口的实现:
在arch/arm/include/asm/atomic.h中实现了对原子操作接口的实现(3.10):
atomic_read/atomic_set:
- /* * On ARM, ordinary assignment (str instruction) doesn't clear the local
- * strex/ldrex monitor on some implementations. The reason we can use it for
- * atomic_set() is the clrex or dummy strex done on every exception return.
- */
- #define atomic_read(v) (*(volatile int *)&(v)->counter)
- #define atomic_set(v,i) (((v)->counter) = (i))
atomic_add:
- /** ARMv6 UP and SMP safe atomic ops. We use load exclusive and store exclusive to ensure that these are atomic. We may loop to ensure that the update happens. */
- static inline void atomic_add(int i, atomic_t *v)
- {
- unsigned long tmp;
- int result;
- __asm__ __volatile__("@ atomic_add\n"
- "1: ldrex %0, [%3]\n"
- " add %0, %0, %4\n"
- " strex %1, %0, [%3]\n"
- " teq %1, #0\n"
- " bne 1b"
- : "=&r" (result), "=&r" (tmp), "+Qo" (v->counter)
- : "r" (&v->counter), "Ir" (i)
- : "cc");
- }
atomic_add_return:
- static inline int atomic_add_return(int i, atomic_t *v)
- {
- unsigned long tmp;
- int result;
- smp_mb();
- __asm__ __volatile__("@ atomic_add_return\n"
- "1: ldrex %0, [%3]\n"
- " add %0, %0, %4\n"
- " strex %1, %0, [%3]\n"
- " teq %1, #0\n"
- " bne 1b"
- : "=&r" (result), "=&r" (tmp), "+Qo" (v->counter)
- : "r" (&v->counter), "Ir" (i)
- : "cc");
- smp_mb();
- return result;
- }
atomic_sub:
- static inline void atomic_sub(int i, atomic_t *v)
- {
- unsigned long tmp;
- int result;
- __asm__ __volatile__("@ atomic_sub\n"
- "1: ldrex %0, [%3]\n"
- " sub %0, %0, %4\n"
- " strex %1, %0, [%3]\n"
- " teq %1, #0\n"
- " bne 1b"
- : "=&r" (result), "=&r" (tmp), "+Qo" (v->counter)
- : "r" (&v->counter), "Ir" (i)
- : "cc");
- }