Linux内核锁实现原理,glibc读写锁与linux内核读写自旋锁的原理

1.glibc读写锁的原理 加读锁: int __pthread_rwlock_rdlock(pthread_rwlock_t* rwlock); (nptl/pthread_rwlock_rdlock.c) 加写锁: int __pthread_rwlock_wrlock(pthread_rwlock_t* rwlock) (nptl/pthread_rwlock_wrlock.c) 解锁: int __pthread_rwlock_un

1.glibc读写锁的原理

加读锁:

int __pthread_rwlock_rdlock(pthread_rwlock_t* rwlock);

(nptl/pthread_rwlock_rdlock.c)

加写锁:

int __pthread_rwlock_wrlock(pthread_rwlock_t* rwlock)

(nptl/pthread_rwlock_wrlock.c)

解锁:

int __pthread_rwlock_unlock(pthread_rwlock_t* rwlock)

(nptl/pthread_rwlock_wrlock.c)

默认读写锁是读优先的,如果有写者在等待锁,不会阻塞读者。

如果配置为写优先,如果有写者在等待锁,所有的后续的读者加锁都会阻塞。

但是当占有读者锁的读者数为0时,当有写者在等待时,会优先唤醒写者。

当配置为读者优先时,会出现写者饿死的情况

当配置为写者优先时,会出现读者饿死的情况

读写锁适用于读多写少的场景

1)加读锁源码

int __pthread_rwlock_rdlock(pthread_rwlock_t* rwlock)

{

...

//进入临界区

lll_lock (rwlock->__data.__lock, rwlock->__data.__shared);

while (1)

{

if (rwlock->__data.__writer == 0

&& (!rwlock->__data.__nr_writers_queued || PTHREAD_RWLOCK_PREFER_READER_P (rwlock)))

{//当写者数为0,或者有写者在等待锁,但是配置的是读优先,则可以抢占,否则阻塞读者

if (__builtin_expect (++rwlock->__data.__nr_readers == 0, 0))

{//读者数+1,当超过上限溢出时,则返回EAGAIN

--rwlock->__data.__nr_readers;

result = EAGAIN;

break;

}

else

LIBC_PROBE (rdlock_acquire_read, 1, rwlock);

break;

}

....

if (__buildtin_expect (++rwlock->__data.__nr_readers_queued == 0, 0)) //排队读者数+1

{//读者等待数+1,当溢出时,则返回EAGAIN

--rwlock->__data.__nr_readers_queued;

result = EAGAIN;

break;

}

....

lll_unlock (rwlock->__data.__lock, rwlock->__data.__shared);

lll_futex_wait(&rwlock->__data.__readers_wakeup, waitval, rwlock->__data.shared); //等待唤醒

lll_lock (rwlock->__data.__lock, rwlock->__data.__shared);

--rwlock->__data.__nr_readers_queued; //排队读者数-1

}//end while

lll_unlock (rwlock->__data.__lock, rwlock->__data.__shared);

}

2)加写锁源码

int __pthread_rwlock_wrlock (pthread_rwlock_t* rwlock)

{

.....

lll_lock (rwlock->__data.__lock, rwlock->__data.__shared);

while (1)

{

if (rwlock->__data.__writer == 0 && rwlock->__data.__nr_readers == 0)

{//写者数为0,读者数为0,加写锁成功

LIBC_PROBE (wrlock_acquire_write, 1, rwlock);

break;

}

.....

if (++rwlock->__data.__nr_writers_queued == 0)

{//写者等待数+1,当溢出时,则返回EAGAIN

--rwlock->__data.__nr_writers_queued;

result = EAGAIN;

break;

}

lll_unlock (rwlock->__data.__lock, rwlock->__data.__shared);

lll_futex_wait(&rwlock->__data.__readers_wakeup, waitval, rwlock->__data.shared); //等待唤醒

lll_lock (rwlock->__data.__lock, rwlock->__data.__shared);

--rwlock->__data.__nr_writers_queued; //排队写者数-1

}

lll_unlock (rwlock->__data.__lock, rwlock->__data.__shared);

}

3)解锁源码

int __pthread_rwlock_unlock (pthread_rwlock_t* rwlock)

{

......

lll_lock (rwlock->__data.__lock, rwlock->__data.__shared);

if (rwlock->__data.__writer) //写者数不为0

rwlock->__data.__writer = 0;

else //读者数不为0

--rwlock->__data.__nr_readers;

if (rwlock->__data.__nr_readers == 0) //没有读者

{

if (rwlock->__data.__nr_writers_queued) //有写者在等待

{

++rwlock->__data.__writer_wakeup; //唤醒写者

lll_unlock (rwlock->__data.__lock, rwlock->__data.__shared);

lll_futex_wake (&rwlock->__data.__writer_wakeup, 1,

rwlock->__data.__shared);

return 0;

}

else if (rwlock->__data.__nr_readers_queued) //有读者等待

{

++rwlock->__data.__readers_wakeup; //唤醒读者

lll_unlock (rwlock->__data.__lock, rwlock->__data.__shared);

lll_futex_wake (&rwlock->__data.__readers_wakeup, INT_MAX,

rwlock->__data.__shared);

return 0;

}

}

lll_unlock (rwlock->__data.__lock, rwlock->__data.__shared);

}

2.内核读写自旋锁

linux内核的读写自旋锁也是读优先的。

以2.6.18内核的实现为例

1)加读锁:

int __lockfunc generic__raw_read_trylock(raw_rwlock_t* lock)

|—>__raw_read_lock(lock)

|—>__build_read_lock(rw, "__read_lock_failed");

|—>__build_read_lock_const(rw, "__read_lock_failed")

当lock初始化时,初始化为RW_LOCK_BIAS(0×01000000),所以内核读写锁最多支持001000000个读者)

__build_read_lock_const实现:

#define __build_read_lock_const(rw, helper) \

asm volatile(LOCK_PREFIX " subl $1,%0\n\t" \ //锁总线,rw-1

"jns 1f\n" \ 如果非负,加读锁成功,跳转到1(因为加写锁时,会直接rw - RW_LOCK_BIAS,所以再减1会变为负数)

"pushl %%eax\n\t" \

"leal %0,%%eax\n\t" \

"call " helper "\n\t" \ 如果为负,加读锁失败,跳转到__read_lock_failed

"popl %%eax\n\t" \

"1:\n" \

:"+m" (*(volatile int *)rw) : : "memory")

__read_lock_failed实现

asm(

".section .sched.text\n"

".align 4\n"

".globl __read_lock_failed\n"

"__read_lock_failed:\n\t"

LOCK_PREFIX "incl (%eax)\n"

"1: rep; nop\n\t"

"cmpl $1,(%eax)\n\t" //循环等待,直到rw变为0x01000000

"js 1b\n\t"

LOCK_PREFIX "decl (%eax)\n\t"

"js __read_lock_failed\n\t"

"ret"

);

2)解读锁:

_read_unlock()

|—>__raw_read_unlock()

static inline void __raw_read_unlock(raw_rwlock_t *rw)

{

asm volatile(LOCK_PREFIX "incl %0" :"+m" (rw->lock) : : "memory");

}

3)加写锁:

__write_trylock(rwlock_t* lock)

|—>__raw_write_lock(lock)

|—>__build_write_lock(rw, "__write_lock_failed")

|—>__build_write_lock_const(rw, "__writer_lock_failed");

__build_write_lock_const实现

#define __build_write_lock_const(rw, helper) \

asm volatile(LOCK_PREFIX " subl $" RW_LOCK_BIAS_STR ",%0\n\t" \ //rw - RW_LOCK_BIAS

"jz 1f\n" \ //等于0,加锁成功

"pushl %%eax\n\t" \

"leal %0,%%eax\n\t" \

"call " helper "\n\t" \ 不成功,跳转__writer_lock_failed

"popl %%eax\n\t" \

"1:\n" \

:"+m" (*(volatile int *)rw) : : "memory")

__write_lock_failed实现

asm(

".section .sched.text\n"

".align 4\n"

".globl __write_lock_failed\n"

"__write_lock_failed:\n\t"

LOCK_PREFIX "addl $" RW_LOCK_BIAS_STR ",(%eax)\n"

"1: rep; nop\n\t"

"cmpl $" RW_LOCK_BIAS_STR ",(%eax)\n\t"

"jne 1b\n\t"

LOCK_PREFIX "subl $" RW_LOCK_BIAS_STR ",(%eax)\n\t" //直到rw-RW_LOCK_BIAS_STR=0,加锁成功,否则循环行等待

"jnz __write_lock_failed\n\t"

"ret"

);

4)解写锁:

_write_unlock()

|–>__raw_write_unlock(lock)

static inline void __raw_write_unlock(raw_rwlock_t *rw)

{

asm volatile(LOCK_PREFIX "addl $" RW_LOCK_BIAS_STR ", %0"

: "+m" (rw->lock) : : "memory"); //rw + 1

}

3.参考文章:

1)linux下写优先的读写锁的设计:http://www.ibm.com/developerworks/cn/linux/l-rwlock_writing/

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值