关于linux同步机制
定义
确保多个进程安全操作共享数据的安全机制,常用的有以下几类:
名称 | 定义 | 优点 | 缺点 |
原子操作 | 保证指令以原子的方式执行,即不能被打断 | 简单易用 | 使用面窄,只适用于计数器场合 |
自旋锁 | 最多只能被一个执行线程占有,其它试图占用此锁的线程将循环等待 | 可以用于中断处理程序中 | 循环等待占用CPU的时间,适用于快速任务(锁占用时间<线程切换时间) |
信号量 | 睡眠锁,有任务试图获取已被占用的锁时,会才采用睡眠等待的方式 | 睡眠挂起释放了CPU的控制权,提高程序执行效率 | 不能用于中断处理程序,会导致睡眠 |
代码分析
内核版本:2.6.28.8
原子锁
代码出处: /arch/x86/include/asm/atomic_32.h
数据结构
typedef struct {
int counter;
} atomic_t;
仅例举原子整数操作列表
在声明一个atomic_t变量时,将它初始化为i
#define ATOMIC_INIT(i) { (i) }
原子的读取整数变量v
#define atomic_read(v) ((v)->counter)
原子地设置v值为i
#define atomic_set(v, i) (((v)->counter) = (i))
原子地给V加i
static inline void atomic_add(int i, atomic_t *v)
{
asm volatile(LOCK_PREFIX "addl %1,%0"
: "+m" (v->counter)
: "ir" (i));
}
原子地给V减i
static inline void atomic_sub(int i, atomic_t *v)
{
asm volatile(LOCK_PREFIX "subl %1,%0"
: "+m" (v->counter)
: "ir" (i));
}
原子地给V加1
static inline void atomic_inc(atomic_t *v)
{
asm volatile(LOCK_PREFIX "incl %0"
: "+m" (v->counter));
}
原子地给V减1
static inline void atomic_dec(atomic_t *v)
{
asm volatile(LOCK_PREFIX "decl %0"
: "+m" (v->counter));
}
原子地给V减i,如果结果等于0,返回真,反之为假
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;
}
原子地给V加i,如果结果为负数,返回真,反之为假
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;
}
原子地给V减1,如果结果等于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;
}
原子地给V加i,如果结果等于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;
}
自旋锁
数据结构
代码出处: /include/linux/spinlock_types.h
typedef struct {
raw_spinlock_t raw_lock;
#ifdef CONFIG_GENERIC_LOCKBREAK
unsigned int break_lock;
#endif
#ifdef CONFIG_DEBUG_SPINLOCK
unsigned int magic, owner_cpu;
void *owner;
#endif
#ifdef CONFIG_DEBUG_LOCK_ALLOC
struct lockdep_map dep_map;
#endif
} spinlock_t;
方法列举
代码出处: /include/linux/spinlock.h和 /include/linux/spinlock_api_up.h 和/arch/um/include/asm/system.h
spin_lock 获取自旋锁
调用preempt_disable隐式禁止抢占
#define spin_lock(lock) /
do { preempt_disable(); __acquire(lock); (void)(lock); } while (0)
spin_lock_irq 禁止本地中断并获取自旋锁
local_irq_disable禁止本地中断
#define spin_lock_irq(lock) /
do { local_irq_disable(); __LOCK(lock); } while (0)
spin_lock_irqsave 保存本地中断的当前状态,禁止本地中断并获取自旋锁
#define spin_lock_irqsave(lock, flags) /
do { /
typecheck(unsigned long, flags); /
_spin_lock_irqsave(lock, flags); /
} while (0)
#define _spin_lock_irqsave(lock, flags) /
do { local_irq_save(flags); __LOCK(lock); } while (0)
local_save_flags保存当前进程的状态
#define local_irq_save(flags) do { local_save_flags(flags); /
local_irq_disable(); } while(0)
spin_unlock 释放自旋锁
# define spin_unlock(lock) /
do {__raw_spin_unlock(&(lock)->raw_lock); __release(lock); } while (0)
static inline void __raw_spin_unlock(raw_spinlock_t *lock)
{
__asm__ volatile ("move.d %1,%0" /
: "=m" (lock->slock) /
: "r" (1) /
: "memory");
}
spin_unlock_irq 释放自旋锁,并激活本地中断
local_irq_enable使能本地中断
调用preempt_disable给抢占计数器-1,当其为0时使能抢占
#define spin_unlock_irq(lock) /
do { local_irq_enable(); __UNLOCK(lock); } while (0)
#define __UNLOCK(lock) /
do { preempt_enable(); __release(lock); (void)(lock); } while (0)
spin_unlock_irqrestore 释放自旋锁,并让中断恢复到以前状态
local_irq_restore恢复以前进程的状态
#define spin_unlock_irqrestore(lock, flags) /
do { /
typecheck(unsigned long, flags); /
_spin_unlock_irqrestore(lock, flags); /
} while (0)
#define _spin_unlock_irqrestore(lock, flags) __UNLOCK_IRQRESTORE(lock, flags)
#define __UNLOCK_IRQRESTORE(lock, flags) /
do { local_irq_restore(flags); __UNLOCK(lock); } while (0)
#define local_irq_restore(flags) do { typecheck(unsigned long, flags); /
set_signals(flags); } while(0)
spin_lock_init 初始化自旋锁
# define spin_lock_init(lock) /
do { /
static struct lock_class_key __key; /
/
__spin_lock_init((lock), #lock, &__key); /
} while (0)
void __spin_lock_init(spinlock_t *lock, const char *name,
struct lock_class_key *key)
{
#ifdef CONFIG_DEBUG_LOCK_ALLOC
/*
* Make sure we are not reinitializing a held lock:
*/
debug_check_no_locks_freed((void *)lock, sizeof(*lock));
lockdep_init_map(&lock->dep_map, name, key, 0);
#endif
lock->raw_lock = (raw_spinlock_t)__RAW_SPIN_LOCK_UNLOCKED;
lock->magic = SPINLOCK_MAGIC;
lock->owner = SPINLOCK_OWNER_INIT;
lock->owner_cpu = -1;
}
spin_trylock试图获取指定的锁,如未获取,返回非0
#define spin_trylock(lock) __cond_lock(lock, _spin_trylock(lock))
int __lockfunc _spin_trylock(spinlock_t *lock)
{
preempt_disable();
if (_raw_spin_trylock(lock)) {
spin_acquire(&lock->dep_map, 0, 1, _RET_IP_);
return 1;
}
preempt_enable();
return 0;
}
如果指定的锁正在被获取,返回非0
#define spin_is_locked(lock) __raw_spin_is_locked(&(lock)->raw_lock)
static inline int __raw_spin_is_locked(raw_spinlock_t *x)
{
volatile unsigned int *a = __ldcw_align(x);
return *a == 0;
}
信号量
代码出处: /include/linux/semaphore.h
数据结构
struct semaphore {
spinlock_t lock;
unsigned int count;
struct list_head wait_list;
};
sema_init 初始化信号量
static inline void sema_init(struct semaphore *sem, int val)
{
static struct lock_class_key __key;
*sem = (struct semaphore) __SEMAPHORE_INITIALIZER(*sem, val);
lockdep_init_map(&sem->lock.dep_map, "semaphore->lock", &__key, 0);
}
init_MUTEX 以1值初始化互斥量
#define init_MUTEX(sem) sema_init(sem, 1)
init_MUTEX_LOCKED 以0值初始化互斥量,初始为加锁状态
#define init_MUTEX_LOCKED(sem) sema_init(sem, 0)
down_interruptible 试图获得指定的信号量,如果信号量已被用,则进入可中断睡眠状态
int down_interruptible(struct semaphore *sem)
{
unsigned long flags;
int result = 0;
spin_lock_irqsave(&sem->lock, flags);
if (likely(sem->count > 0))
sem->count--;
else
result = __down_interruptible(sem);
spin_unlock_irqrestore(&sem->lock, flags);
return result;
}
down_interruptible 试图获得指定的信号量,如果信号量已被用,则进入不可中断睡眠状态
void down(struct semaphore *sem)
{
unsigned long flags;
spin_lock_irqsave(&sem->lock, flags);
if (likely(sem->count > 0))
sem->count--;
else
__down(sem);
spin_unlock_irqrestore(&sem->lock, flags);
}
down_interruptible 试图获得指定的信号量,如果信号量已被用,立刻返回非0值
int down_trylock(struct semaphore *sem)
{
unsigned long flags;
int count;
spin_lock_irqsave(&sem->lock, flags);
count = sem->count - 1;
if (likely(count >= 0))
sem->count = count;
spin_unlock_irqrestore(&sem->lock, flags);
return (count < 0);
}
Up 释放指定的信号量,如果睡眠等待队列非空,唤醒其中一个任务
void up(struct semaphore *sem)
{
unsigned long flags;
spin_lock_irqsave(&sem->lock, flags);
if (likely(list_empty(&sem->wait_list)))
sem->count++;
else
__up(sem);
spin_unlock_irqrestore(&sem->lock, flags);
}