5. 读写锁

  • 0 概述
  • 1 读者信号量
  • 3 小结

0 概述

在阅读本节前请思考如下小问题。

  • 什么时候使用读者信号量,什么时候使用写者信号量,由什么来判断?
  • 读写信号量使用的自旋等待机制(optimistic spinning) 是如何实现的?

上述介绍的信号量有一个明显的缺点 — 没有区分临界区的读写属性读写锁通常允许多个线程并发地读(!!!)访问临界区,但是写访问只限制于一个线程。读写锁能有效地提高并发性,在多处理器系统中允许同时有多个读者访问共享资源,但写者是排他性的.

读写锁具有如下特性

  • 允许多个读者同时进入临界区,但同一时刻写者不能进入
  • 同一时刻只允许一个写者进入临界区。
  • 读者和写者不能同时进入临界区

读写锁有两种,分别是spinlock类型信号量类型

spinlock类型的读写锁数据结构定义在include/linux/rwlock_types.h头文件中。

[include/linux/rwlock_types.h]
typedef struct {
	arch_rwlock_t raw_lock;
} rwlock_t;

[arch/arm/include/asm/spinlock_types.h]
typedef struct {
	u32 lock;
} arch_rwlock_t;

常用的函数如下:

[include/linux/rwlock.h]

spinlock锁一样,读写锁关闭中断下半部的版本spinlock类型的读写锁实现比较简单,本章重点关注信号量类型读写锁的实现。

1 读者信号量

读写信号量的定义如下:

[include/linux/rwsem.h]
  • wait_lock是一个spinlock变量,用于实现对读写信号量数据结构中count成员的 原子操作和保护。
  • count用于表示读写信号量的计数。以前读写信号量的实现用activity来表示,activity=0表示没有读者和写者,activity=-l表示有写者,activity>0表示有读者。现在count的计数方法己经发生了变化。
  • wait_list链表用于管理所有在该信号量上睡眠的进程,没有成功获取锁的进程会睡眠在这个链表上。
  • osq: MCS锁,在第4.4节中己详细介绍。
  • owner: 当写者成功获取锁时,owner指向锁持有者的task_struct数据结构。

count成员的语义定义如下:

[]

上述的宏定义看起来比较复杂,翻译成十进制数值会清晰一些,本章以ARM32体系架构为例介绍读写信号量的实现。

3 小结

信号量缺点: 没有区分临界区的读写属性

读写锁特点:

  • 允许多个读者同时进入临界区,但同一时刻写者不能进入
  • 同一时刻只允许一个写者进入临界区。
  • 读者和写者不能同时进入临界区

读写锁有两种,分别是spinlock类型信号量类型。分别对应typedef struct rwlock_t和struct rw_semaphore.

读写锁在内核中应用广泛,特别是在内存管理中,全局的mm->mmap_sem读写信号量用于保护进程地址空间的一个读写信号量,还有反向映射RMAP系统中的anon_vma->rwsem,地址空间address_space数据结构中i_mmap_rwsem等。

再次总结读写锁的重要特性

  • down_read(): 如果一个进程持有了读者锁,那么允许继续申请多个读者锁,申请写者锁则要睡眠等待
  • down_write(): 如果一个进程持有了写者锁,那么第二个进程申请该写者锁自旋等待(配置了CONFIG_RWSEM_SPIN_ON_OWNER选项不睡眠),申请读者锁则要睡眠等待
  • up_write()/up_read():如果等待队列第一个成员是写者,那么唤醒该写者,否则唤醒排在等待队列最前面连续的几个读者

你可能感兴趣的:(5. 读写锁)