linux 内核中的rwlock类型的读写锁是基于spinlock来实现的。
读写锁具有如下特性:
- 允许多个读者同时进入临界区,但同一时刻写者不能进入。
- 同一时刻只允许一个写者进入临界区。
- 读者和写者不能同时进入临界区。
1、spinlock类型读写锁
注意,spinlock类型读写锁,具有spinlock的特性及在lock时关闭内核抢占,在unlock时开启内核抢占。
include/linux/rwlock_types.h
/*spinlock实现的读写锁*/
typedef struct {
arch_rwlock_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
} rwlock_t;
arch/arm/include/asm/spinlock_types.h
typedef struct {
u32 lock;
} arch_rwlock_t;
#define __ARCH_RW_LOCK_UNLOCKED { 0 }
1.1、读写锁初始化
include/linux/rwlock.h
# define rwlock_init(lock) \
do { *(lock) = __RW_LOCK_UNLOCKED(lock); } while (0)
#define __RW_LOCK_UNLOCKED(lockname) \
(rwlock_t) { .raw_lock = __ARCH_RW_LOCK_UNLOCKED, \
RW_DEP_MAP_INIT(lockname) }
#define DEFINE_RWLOCK(x) rwlock_t x = __RW_LOCK_UNLOCKED(x)
1.2、读者自旋锁
1.2.1、read_lock
#define read_lock(lock) _raw_read_lock(lock)
void __lockfunc _raw_read_lock(rwlock_t *lock)
{
__raw_read_lock(lock);
}
static inline void __raw_read_lock(rwlock_t *lock)
{
/*关闭内核抢占*/
preempt_disable();
rwlock_acquire_read(&lock->dep_map, 0, 0, _RET_IP_);
/*do_raw_read_lock*/
LOCK_CONTENDED(lock, do_raw_read_trylock, do_raw_read_lock);
}
# define do_raw_read_lock(rwlock) do {__acquire(lock); arch_read_lock(&(rwlock)->raw_lock); } while (0)
/*
* Read locks are a bit more hairy:
* - Exclusively load the lock value.
* - Increment it. (加一)
* - Store new lock value if positive(正数), and we still own this location.
* If the value is negative(负数), we've already failed.
* - If we failed to store the value, we want a negative result.
* - If we failed, try again.
* Unlocking is similarly hairy. We may have multiple read locks
* currently active. However, we know we won't have any write
* locks.
*/
static inline void arch_read_lock(arch_rwlock_t *rw)
{
unsigned long tmp, tmp2;
prefetchw(&rw->lock);
__asm__ __volatile__(
"1: ldrex %0, [%2]\n" /*tmp = rw->lock*/
" adds %0, %0, #1\n" /*tmp += 1,adds执行加法,更新cpsr寄存器中条件标志位(C,N,Z...)*/
" strexpl %1, %0, [%2]\n" /*pl条件的含义:Plus,positive or zero,条件N == 0,即不为负数(>=0)条件才执行strex指令;tmp >= 0则rw->lock=tmp(原子地实现rw->lock++)*/
WFE("mi") /*MI条件的含义:Minus, negative,条件N == 1,即为负数(<0),说明tmp+=1后溢出或则写者抢先写入0x8000000,导致符号位为1;wfemi,cpu进入休眠*/
" rsbpls %0, %1, #0\n" /*RSB(Reverse Subtraction):反向减法(tmp=0-tmp2),pl为条件位(tmp>=0),s为结果更新cpsr寄存器中条件标志位(C,N,Z...);tmp2=0,独占访问成功,tmp2=1,独占访问失败*/
" bmi 1b" /*负数条件下(独占访问失败)跳转到标签1处*/
: "=&r" (tmp), "=&r" (tmp2)
: "r" (&rw->lock)
: "cc");
smp_mb();
}
1.2.2、read_trylock
#define read_trylock(lock) __cond_lock(lock, _raw_read_trylock(lock))
int __lockfunc _raw_read_trylock(rwlock_t *lock)
{
return __raw_read_trylock(lock);
}
static inline int __raw_read_trylock(rwlock_t *lock)
{
/*关闭内核抢占*/
preempt_disable();
if (do_raw_read_trylock(lock)) {
rwlock_acquire_read(&lock->dep_map, 0, 1, _RET_IP_);
return 1;
}
/*开启内核抢占*/
preempt_enable();
return 0;
}
# define do_raw_read_trylock(rwlock) arch_read_trylock(&(rwlock)->raw_lock)
static inline int arch_read_trylock(arch_rwlock_t *rw)
{
unsigned long contended, res;
prefetchw(&rw->lock);
do {
__asm__ __volatile__(
" ldrex %0, [%2]\n" /*contended = rw->lock*/
" mov %1, #0\n" /*res = 0*/
" adds %0, %0, #1\n" /*contended += 1*/
" strexpl %1, %0, [%2]" /*注意pl的条件(contended>=0),rw->lock = contended*/
: "=&r" (contended), "=&r" (res)
: "r" (&rw->lock)
: "cc");
} while (res);
/* If the lock is negative, then it is already held for write. */
if (contended < 0x80000000) { /*获取锁成功返回1*/
smp_mb();
return 1;
} else {
return 0;
}
}
1.2.3、read_unlock
#define read_unlock(lock) _raw_read_unlock(lock)
void __lockfunc _raw_read_unlock(rwlock_t *lock)
{
__raw_read_unlock(lock);
}
static inline void __raw_read_unlock(rwlock_t *lock)
{
rwlock_release(&lock->dep_map, 1, _RET_IP_);
do_raw_read_unlock(lock);
/*开启内核抢占*/
preempt_enable();
}
# define do_raw_read_unlock(rwlock) do {arch_read_unlock(&(rwlock)->raw_lock); __release(lock); } while (0)
static inline void arch_read_unlock(arch_rwlock_t *rw)
{
unsigned long tmp, tmp2;
smp_mb();
prefetchw(&rw->lock);
__asm__ __volatile__(
"1: ldrex %0, [%2]\n" /*tmp = rw->lock*/
" sub %0, %0, #1\n" /*tmp -= 1*/
" strex %1, %0, [%2]\n" /*w->lock = tmp*/
" teq %1, #0\n" /*teq操作指的是测试相等性,它通过对两个操作数执行异或操作,并更新条件标志,但不保存结果.测试tmp2是否为0*/
" bne 1b" /*独占访问失败,跳转到标签1处*/
: "=&r" (tmp), "=&r" (tmp2)
: "r" (&rw->lock)
: "cc");
if (tmp == 0) /*tmp==0,表明所有的读者都释放了锁,通过sev唤醒写者(如果存在的话)*/
dsb_sev();
}
1.3、写者自旋锁
1.3.1、write_lock
#define write_lock(lock) _raw_write_lock(lock)
void __lockfunc _raw_write_lock(rwlock_t *lock)
{
__raw_write_lock(lock);
}
include/linux/rwlock_api_smp.h
static inline void __raw_write_lock(rwlock_t *lock)
{
/*关闭内核抢占*/
preempt_disable();
rwlock_acquire(&lock->dep_map, 0, 0, _RET_IP_);
/*do_raw_write_lock*/
LOCK_CONTENDED(lock, do_raw_write_trylock, do_raw_write_lock);
}
# define do_raw_write_lock(rwlock) do {__acquire(lock); arch_write_lock(&(rwlock)->raw_lock); } while (0)
arch/arm/include/asm/spinlock.h
/*
* RWLOCKS
* Write locks are easy - we just set bit 31. When unlocking, we can
* just write zero since the lock is exclusively held(锁被独占).
*/
static inline void arch_write_lock(arch_rwlock_t *rw)
{
unsigned long tmp;
prefetchw(&rw->lock);
__asm__ __volatile__(
"1: ldrex %0, [%1]\n" /*tmp=rw->lock*/
" teq %0, #0\n" /*测试tmp是否为0*/
WFE("ne") /*tmp不为0,表示锁被其他任务持有,则CPU核进入睡眠状态*/
" strexeq %0, %2, [%1]\n" /*相等则rw->lock=0x80000000*/
" teq %0, #0\n" /*测试tmp是否为0*/
" bne 1b" /*tmp不为0跳转到标签1处继续执行*/
: "=&r" (tmp)
: "r" (&rw->lock), "r" (0x80000000)
: "cc");
smp_mb();
}
1.3.2、write_trylock
#define write_trylock(lock) __cond_lock(lock, _raw_write_trylock(lock))
int __lockfunc _raw_write_trylock(rwlock_t *lock)
{
return __raw_write_trylock(lock);
}
static inline int __raw_write_trylock(rwlock_t *lock)
{
preempt_disable();
if (do_raw_write_trylock(lock)) {
rwlock_acquire(&lock->dep_map, 0, 1, _RET_IP_);
return 1;
}
preempt_enable();
return 0;
}
# define do_raw_write_trylock(rwlock) arch_write_trylock(&(rwlock)->raw_lock)
static inline int arch_write_trylock(arch_rwlock_t *rw)
{
unsigned long contended, res;
prefetchw(&rw->lock);
do {
__asm__ __volatile__(
" ldrex %0, [%2]\n" /*contended = rw->lock*/
" mov %1, #0\n" /*res = 0,除了独占访问失败需要尝试,其他情况如锁已被持有则返回0*/
" teq %0, #0\n" /*测试contended是否为0*/
" strexeq %1, %3, [%2]" /*contended为0的条件下(锁空闲),设置rw->lock=0x80000000表示写者持锁,设置失败res=1,需要继续尝试*/
: "=&r" (contended), "=&r" (res)
: "r" (&rw->lock), "r" (0x80000000)
: "cc");
} while (res); /*独占访问失败则继续尝试*/
if (!contended) { /*获取锁成功返回1*/
smp_mb();
return 1;
} else { /*获取锁失败返回0*/
return 0;
}
}
1.3.3、write_unlock
#define write_unlock(lock) _raw_write_unlock(lock)
void __lockfunc _raw_write_unlock(rwlock_t *lock)
{
__raw_write_unlock(lock);
}
static inline void __raw_write_unlock(rwlock_t *lock)
{
rwlock_release(&lock->dep_map, 1, _RET_IP_);
do_raw_write_unlock(lock);
/*开启内核抢占*/
preempt_enable();
}
# define do_raw_write_unlock(rwlock) do {arch_write_unlock(&(rwlock)->raw_lock); __release(lock); } while (0)
static inline void arch_write_unlock(arch_rwlock_t *rw)
{
smp_mb();
__asm__ __volatile__(
"str %1, [%0]\n" /*rw->lock=0*/
:
: "r" (&rw->lock), "r" (0)
: "cc");
dsb_sev(); /*写者释放锁,唤醒其他休眠等待锁的读者/写者cpu*/
}
1.4、衍生的其他读写锁
read_lock_irq(lock) / read_unlock_irq(lock)
write_lock_irq(lock) / write_unlock_irq(lock)
read_lock_irqsave(lock, flags) / read_unlock_irqrestore(lock, flags)
write_lock_irqsave(lock, flags) / write_unlock_irqrestore(lock, flags)