内核读写信号量

内核中的读写信号量

互斥信号量(struct semaphore),没有区分数据的读写访问。对于大多数的应用场景,读并发都是远高于写并发的。此场景下,互斥信号量就不是十分适用了。

在Linux内核中,读写信号量(Read-Write Semaphore)是一种用于 多读者-单写者 场景的同步机制。相比互斥信号量,它允许多个读取者并行访问,但写入者需要独占访问。它提供了更高的并发性,相对于普通信号量。

普通信号量主要是用来管理资源池;互斥信号量用于互斥,不区分读者/写者;而读写信号量主要是用于读者/写者的互斥。

读写信号量的基本原理

  • 读操作:多个读者可以同时获取读写信号量,这允许多个读者同时访问共享资源。
  • 写操作:写者必须独占地获取读写信号量,这意味着在写者写入共享资源时,所有其他读者和写者都必须等待。

数据结构

/* All arch specific implementations share the same struct */
struct rw_semaphore {
	atomic_long_t count; // 计数器,管理读/写访问权限
	struct list_head wait_list; // 等待队列,存放等待获取信号量的任务
	raw_spinlock_t wait_lock; // 保护 wait_list 的自旋锁
#ifdef CONFIG_RWSEM_SPIN_ON_OWNER
	struct optimistic_spin_queue osq; /* spinner MCS lock 乐观自旋锁,提高性能 */
	/*
	 * Write owner. Used as a speculative check to see
	 * if the owner is running on the cpu.
	 */
	struct task_struct *owner; // 记录当前持有写锁的任务
#endif
#ifdef CONFIG_DEBUG_LOCK_ALLOC
	struct lockdep_map	dep_map;
#endif
};
字段作用
count核心计数器,决定当前信号量的状态(正数=读者数量,负数=写者占用)
wait_list等待队列,存放等待获取信号量的任务
wait_lock自旋锁,用于保护 wait_list,避免竞争
osq乐观自旋锁,用于优化写者的性能,减少调度延迟
owner当前持有写锁的任务,用于优化自旋锁

<font style="color:rgb(64, 64, 64);">atomic_long_t count</font>

  • 作用:这是一个原子变量,用于记录读写信号量的状态。
  • 实现原理
    • 正值:表示有多个读者持有锁。值为 <font style="color:rgb(0, 0, 0);">n</font> 表示有 <font style="color:rgb(0, 0, 0);">n</font> 个读者。
    • 负值:表示有一个写者持有锁。
      • 值为 <font style="color:rgb(0, 0, 0);">-1</font> 表示一个写者持有锁,没有任何等待的读者或写者。
      • 值为 <font style="color:rgb(0, 0, 0);">-N</font><font style="color:rgb(0, 0, 0);">N > 1</font>)时,表示有一个写者持有写锁,并且有 <font style="color:rgb(0, 0, 0);">N-1</font> 个读者或写者在等待获取锁。
    • 0:表示锁未被任何读者或写者持有。
    • 通过原子操作(如 <font style="color:rgb(64, 64, 64);">atomic_long_read</font><font style="color:rgb(64, 64, 64);">atomic_long_cmpxchg</font>)来修改和检查状态。

  • <font style="color:rgb(64, 64, 64);">count</font> 的比特位通常分为以下几个部分:
比特位范围名称描述
<font style="color:rgb(64, 64, 64);">[63:32]</font>(高位)写锁标志和等待线程数量高位用于表示写锁状态和等待线程的数量。
<font style="color:rgb(64, 64, 64);">[31:0]</font>(低位)活跃读者数量低位用于表示当前持有读锁的读者数量。

<font style="color:rgb(64, 64, 64);">struct list_head wait_list</font>

  • 作用:这是一个等待队列,用于存放等待获取锁的线程。
  • 实现原理
    • 当线程无法立即获取锁时,会被加入到 <font style="color:rgb(64, 64, 64);">wait_list</font> 中,并进入睡眠状态。
    • 当锁被释放时,会从 <font style="color:rgb(64, 64, 64);">wait_list</font> 中唤醒等待的进程。

<font style="color:rgb(64, 64, 64);">raw_spinlock_t wait_lock</font>

  • 作用:这是一个自旋锁,用于保护 <font style="color:rgb(64, 64, 64);">wait_list</font> 的并发访问(如添加或移除等待的进程)。
  • 实现原理
    • 在修改 <font style="color:rgb(64, 64, 64);">wait_list</font> 时,需要先获取 <font style="color:rgb(64, 64, 64);">wait_lock</font>

<font style="color:rgb(64, 64, 64);">struct optimistic_spin_queue osq</font>

  • 作用<font style="color:rgb(0, 0, 0);">osq</font> 是一个乐观自旋队列(Optimistic Spin Queue),用于实现一种自适应的自旋锁机制。
  • 实现原理
    • 当线程尝试获取锁时,如果锁被其他线程持有,但持有锁的线程正在运行,当前线程会自旋等待,而不是立即睡眠。自旋时间有限,超时后线程会进入睡眠状态。
    • 这种机制可以减少上下文切换的开销,提高性能。
    • 这种机制通常用于 CPU 密集型场景,以减少锁的获取延迟。

<font style="color:rgb(64, 64, 64);">struct task_struct *owner</font>

  • 作用:记录当前持有写锁的线程。
  • 实现原理
    • 用于实现写锁的优先级继承和乐观自旋。用于快速检查写锁的持有者(task)是否正在运行(即写者是否在 CPU 上运行)。如果写锁的持有者正在运行,则当前task自旋忙等待;否则进行睡眠。
    • 如果写锁的持有者正在运行,其他线程可能会自旋等待,而不是立即睡眠。

读写信号量主要函数

初始化:初始化读写信号量。

void init_rwsem(struct rw_semaphore *sem);

读操作

void down_read(struct rw_semaphore *sem);
int down_read_trylock(struct rw_semaphore *sem);
void up_read(struct rw_semaphore *sem);

写操作

void down_write(struct rw_semaphore *sem);
int down_write_trylock(struct rw_semaphore *sem);
void up_write(struct rw_semaphore *sem);

升级和降级: 读写信号量还支持从读锁升级到写锁,以及从写锁降级到读锁。

int down_read_up_write(struct rw_semaphore *sem);
void up_write_down_read(struct rw_semaphore *sem);

读写信号量的工作机制

读锁获取(<font style="color:rgb(64, 64, 64);">down_read</font>

基本原理

允许多个读者同时访问共享资源,但如果写者持有锁,读者需要等待。

  • 当一个读者尝试获取读锁时,内核会检查 <font style="color:rgb(0, 0, 0);">count</font> 的值:
    • 如果 <font style="color:rgb(0, 0, 0);">count</font> 为正(即已经有读者持有锁),则 <font style="color:rgb(0, 0, 0);">count</font> 加 1,读者直接获取锁。
    • 如果 <font style="color:rgb(0, 0, 0);">count</font> 为 0,则表示锁未被占用,读者获取锁并将 <font style="color:rgb(0, 0, 0);">count</font> 设为 1。
    • 如果 <font style="color:rgb(0, 0, 0);">count</font> 为负(即有一个写者持有锁),则读者需要等待,直到写者释放锁。
代码实现
/*
 * lock for reading
 */
void __sched down_read(struct rw_semaphore *sem)
{
	might_sleep(); // 提示内核当前函数可能会睡眠
	rwsem_acquire_read(&sem->dep_map, 0, 0, _RET_IP_); // 记录锁的获取操作,帮助检测死锁和锁的滥用

	LOCK_CONTENDED(sem, __down_read_trylock, __down_read);
	rwsem_set_reader_owned(sem);
}

EXPORT_SYMBOL(down_read);
<font style="color:rgb(64, 64, 64);">might_sleep()</font>
  • 作用:提示内核当前函数可能会睡眠。
  • 机制
    • 如果当前代码运行在非睡眠环境(如中断上下文),会触发 调试警告,防止死锁。
    • 读写信号量可能导致进程阻塞,因此不能在中断上下文持有自旋锁的情况下调用。
<font style="color:rgb(64, 64, 64);">rwsem_acquire_read(&sem->dep_map, 0, 0, _RET_IP_)</font>
  • 作用:记录读锁的获取操作,用于内核锁调试(Lockdep)。不影响信号量的实际操作。
  • 机制
    • 如果启用了 <font style="color:rgb(64, 64, 64);">CONFIG_DEBUG_LOCK_ALLOC</font>,会记录锁的获取操作,帮助检测死锁和锁的滥用。
<font style="color:rgb(64, 64, 64);">LOCK_CONTENDED(sem, __down_read_trylock, __down_read)</font>
  • 作用:尝试获取读锁,如果失败则进入慢速路径。
  • 参数
    • <font style="color:rgb(64, 64, 64);">sem</font>:读写信号量。
    • <font style="color:rgb(64, 64, 64);">__down_read_trylock</font>:快速路径函数,尝试无竞争地获取读锁。
    • <font style="color:rgb(64, 64, 64);">__down_read</font>:慢速路径函数,处理竞争情况。
  • 机制
    • 首先调用 <font style="color:rgb(64, 64, 64);">__down_read_trylock</font> 尝试快速获取读锁。
    • 如果快速路径失败,调用 <font style="color:rgb(64, 64, 64);">__down_read</font> 进入慢速路径(加入等待队列并睡眠)。
<font style="color:rgb(64, 64, 64);">rwsem_set_reader_owned(sem)</font>
  • 作用:标记当前读锁被读者持有。
  • 机制
    • 如果支持读者持有者跟踪(<font style="color:rgb(64, 64, 64);">CONFIG_RWSEM_READER_OWNED</font>),会设置相关标志。
    • 用于调试和性能分析。
快速路径:<font style="color:rgb(64, 64, 64);">__down_read_trylock</font>
static inline int __down_read_trylock(struct rw_semaphore *sem)
{
	long tmp;

	while ((tmp = atomic_long_read(&sem->count)) >= 0) {
		if (tmp == atomic_long_cmpxchg_acquire(&sem->count, tmp,
				   tmp + RWSEM_ACTIVE_READ_BIAS)) {
			return 1;
		}
	}
	return 0;
}
  • 作用:尝试无竞争地获取读锁。
  • 机制
    • 通过原子操作读取 <font style="color:rgb(64, 64, 64);">count</font>, 判断是否有写锁被持有。
    • 如果没有写锁,通过原子操作增加读计数器<font style="color:rgb(64, 64, 64);">count</font>
    • 如果成功,返回 <font style="color:rgb(64, 64, 64);">1</font>;否则返回 <font style="color:rgb(64, 64, 64);">0</font>。–内核代码其实也有很多待优化之处,此处使用<font style="color:rgb(64, 64, 64);">bool</font>类型更好。
慢速路径:__down_read

__down_read,比较复杂,暂时可以理解为如下伪代码:

static inline void __down_read(struct rw_semaphore *sem)
{
    while (true) {
        long count = atomic_long_read(&sem->count);

        // 检查是否有写锁被持有
        if (count >= 0) {
            // 尝试增加读计数器
            if (atomic_long_try_cmpxchg(&sem->count, &count, count + 1)) {
                break; // 成功获取读锁
            }
        }

        // 如果仍有写锁,继续等待
        raw_spin_lock(&sem->wait_lock);
        list_add_tail(&current->wait_entry, &sem->wait_list);
        raw_spin_unlock(&sem->wait_lock);

        schedule();
    }
}

读锁释放(up_read

基本原理

主要用于 释放读锁,核心作用是减少读者计数,并在必要时唤醒等待的写者

代码实现
/*
 * release a read lock
 */
void up_read(struct rw_semaphore *sem)
{
	rwsem_release(&sem->dep_map, 1, _RET_IP_); // 记录锁的释放操作,用于锁调试(Lockdep)

	__up_read(sem); // 释放读锁
}

EXPORT_SYMBOL(up_read);
/*
 * unlock after reading
 */
static inline void __up_read(struct rw_semaphore *sem)
{
	long tmp;

	tmp = atomic_long_dec_return_release(&sem->count);
	if (unlikely(tmp < -1 && (tmp & RWSEM_ACTIVE_MASK) == 0))
		rwsem_wake(sem);
}
减少读计数器
  • **<font style="color:rgb(64, 64, 64);">tmp = atomic_long_dec_return_release(&sem->count)</font>**
    • 原子地减少 <font style="color:rgb(64, 64, 64);">sem->count</font> 的值,并返回减少后的值。
    • <font style="color:rgb(64, 64, 64);">release</font> 语义确保该操作之前的内存访问不会被重排序到该操作之后。
    • <font style="color:rgb(64, 64, 64);">tmp</font> 是减少后的 <font style="color:rgb(64, 64, 64);">count</font> 值。
检查是否需要唤醒等待线程
  • **<font style="color:rgb(64, 64, 64);">if (unlikely(tmp < -1 && (tmp & RWSEM_ACTIVE_MASK) == 0))</font>**
    • **<font style="color:rgb(64, 64, 64);">tmp < -1</font>**
      • 表示当前有写者持有锁,同时有等待的任务(可能是其他写者或读者)。
    • **<font style="color:rgb(64, 64, 64);">(tmp & RWSEM_ACTIVE_MASK) == 0</font>**
      • 表示没有活跃的读者。
    • **<font style="color:rgb(64, 64, 64);">unlikely</font>**
      • 提示编译器该条件为假的可能性较大,优化分支预测。
  • **<font style="color:rgb(64, 64, 64);">rwsem_wake(sem)</font>**
    • 如果满足条件,调用 <font style="color:rgb(64, 64, 64);">rwsem_wake</font> 唤醒等待队列中的线程。

写锁获取

基本原理
  • 当一个写者尝试获取写锁时,内核会检查 <font style="color:rgb(0, 0, 0);">count</font> 的值:
    • 如果 <font style="color:rgb(0, 0, 0);">count</font> 为 0,则表示锁未被占用,写者获取锁并将 <font style="color:rgb(0, 0, 0);">count</font> 设为 <font style="color:rgb(0, 0, 0);">-1</font>
    • 如果 <font style="color:rgb(0, 0, 0);">count</font> 为正(即有读者持有锁),则写者需要等待,直到所有读者释放锁。
    • 如果 <font style="color:rgb(0, 0, 0);">count</font> 为负(即已经有写者持有锁),则写者需要等待,直到当前写者释放锁。
代码实现
/*
 * lock for writing
 */
void __sched down_write(struct rw_semaphore *sem)
{
	might_sleep(); // 提示内核当前函数可能会睡眠
	rwsem_acquire(&sem->dep_map, 0, 0, _RET_IP_); // 记录锁的获取操作(用于调试)

	LOCK_CONTENDED(sem, __down_write_trylock, __down_write);
	rwsem_set_owner(sem);
}

EXPORT_SYMBOL(down_write);

down_write也是先通过快速路径获取锁,如果失败则进入慢速路径。

**<font style="color:rgb(64, 64, 64);">rwsem_set_owner</font>**:标记当前任务为写锁的持有者,有以下几个作用:

  • 跟踪写锁持有者
    • 其它任务再尝试获取锁时,可以判断占有写锁的任务是否正在CPU上运行(没有睡眠)。
    • 占有写锁的任务是否正在CPU上运行,那么当前尝试获取锁的任务(写者)则通过乐观自旋(optimistic spinning)的方式等待写者释放锁。详情见下图。
  • 优先级继承
    • 如果内核配置了优先级继承(~~<font style="color:rgb(64, 64, 64);">CONFIG_RWSEM_PRIO_AWARE</font>~~),~~<font style="color:rgb(64, 64, 64);">rwsem_set_owner(sem)</font>~~ 会更新写锁持有者的优先级,以避免优先级反转问题。
    • 优先级反转:当一个高优先级任务等待一个低优先级任务持有的锁时,可能会导致高优先级任务被阻塞,低优先级任务执行时间被延长无法及时释放锁(中间有无效的任务切换,浪费CPU资源)。
    • 优先级继承:通过将持有锁的低优先级任务的优先级提升到高优先级线程的水平,确保低优先级任务能够尽快释放锁。
  • 调式支持
    • 如果内核配置了锁调试(<font style="color:rgb(64, 64, 64);">CONFIG_DEBUG_LOCK_ALLOC</font>),<font style="color:rgb(64, 64, 64);">rwsem_set_owner(sem)</font> 会记录写锁的持有者信息,帮助检测死锁和锁的滥用。

快速路径
#define RWSEM_UNLOCKED_VALUE		0x00000000L
#define RWSEM_ACTIVE_BIAS		0x00000001L
#define RWSEM_WAITING_BIAS		(-RWSEM_ACTIVE_MASK-1)
#define RWSEM_ACTIVE_READ_BIAS		RWSEM_ACTIVE_BIAS
#define RWSEM_ACTIVE_WRITE_BIAS		(RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS)

static inline int __down_write_trylock(struct rw_semaphore *sem)
{
	long tmp;

	tmp = atomic_long_cmpxchg_acquire(&sem->count, RWSEM_UNLOCKED_VALUE,
		      RWSEM_ACTIVE_WRITE_BIAS);
	return tmp == RWSEM_UNLOCKED_VALUE;
}
作用
  • 尝试无竞争地获取写锁。
  • 如果成功,返回 <font style="color:rgb(64, 64, 64);">1</font>;否则返回 <font style="color:rgb(64, 64, 64);">0</font>
关键点
  • **<font style="color:rgb(64, 64, 64);">atomic_long_cmpxchg_acquire</font>**
    • 原子地比较并交换 <font style="color:rgb(64, 64, 64);">sem->count</font> 的值。
    • 如果 <font style="color:rgb(64, 64, 64);">sem->count</font> 的当前值等于 <font style="color:rgb(64, 64, 64);">RWSEM_UNLOCKED_VALUE</font>(信号量未被持有),则将其设置为 <font style="color:rgb(64, 64, 64);">RWSEM_ACTIVE_WRITE_BIAS</font>(写者持有信号量)。
慢速路径
/*
 * lock for writing
 */
static inline void __down_write(struct rw_semaphore *sem)
{
	long tmp;

	tmp = atomic_long_add_return_acquire(RWSEM_ACTIVE_WRITE_BIAS,
					     &sem->count);
	if (unlikely(tmp != RWSEM_ACTIVE_WRITE_BIAS))
		rwsem_down_write_failed(sem);
}
作用
  • 尝试获取写锁,如果失败则进入慢速路径。
关键点
  • **<font style="color:rgb(64, 64, 64);">atomic_long_add_return_acquire</font>**
    • 原子地将 <font style="color:rgb(64, 64, 64);">RWSEM_ACTIVE_WRITE_BIAS</font> 加到 <font style="color:rgb(64, 64, 64);">sem->count</font> 上,并返回加后的值。
  • **<font style="color:rgb(64, 64, 64);">tmp != RWSEM_ACTIVE_WRITE_BIAS</font>**
    • 如果加后的值不等于 <font style="color:rgb(64, 64, 64);">RWSEM_ACTIVE_WRITE_BIAS</font>,表示当前存在任务持有信号量,进入慢速路径。
  • **<font style="color:rgb(64, 64, 64);">rwsem_down_write_failed</font>**
    • 处理写锁获取失败的情况,将当前线程加入等待队列并进入睡眠状态。
    • 此函数较复杂,不在这里展开。
    • 如果当前有写者持有锁,则利用乐观自旋进行忙等,<font style="color:rgb(64, 64, 64);">__down_write</font>-><font style="color:rgb(64, 64, 64);">rwsem_down_write_failed</font>-><font style="color:rgb(64, 64, 64);">__rwsem_down_write_failed_common</font>

写锁释放

基本原理
  • 清除当前写锁的所有者:
    • <font style="color:rgb(64, 64, 64);">struct rw_semaphore</font>中的<font style="color:rgb(64, 64, 64);">owner</font>成员设置为<font style="color:rgb(64, 64, 64);">NULL</font>
  • 清除写锁标志
    • 通过原子操作清除 <font style="color:rgb(64, 64, 64);">count</font> 的写锁标志。
  • 唤醒等待的线程
    • <font style="color:rgb(64, 64, 64);">wait_list</font> 中唤醒所有等待的读者和写者,让他们重新尝试获取锁。
代码实现
/*
 * release a write lock
 */
void up_write(struct rw_semaphore *sem)
{
	rwsem_release(&sem->dep_map, 1, _RET_IP_); // 记录锁的释放操作,用于锁调试(Lockdep)

	rwsem_clear_owner(sem); // 设置sem->owner为NULL
	__up_write(sem); // 释放写锁
}

EXPORT_SYMBOL(up_write);
/*
 * unlock after writing
 */
static inline void __up_write(struct rw_semaphore *sem)
{
	if (unlikely(atomic_long_sub_return_release(RWSEM_ACTIVE_WRITE_BIAS,
						    &sem->count) < 0))
		rwsem_wake(sem);
}
  • **<font style="color:rgb(64, 64, 64);">atomic_long_sub_return_release</font>**
    • 原子地将 <font style="color:rgb(64, 64, 64);">RWSEM_ACTIVE_WRITE_BIAS</font><font style="color:rgb(64, 64, 64);">sem->count</font> 中减去,并返回减后的值。
    • <font style="color:rgb(64, 64, 64);">release</font> 语义确保该操作之前的内存访问不会被重排序到该操作之后。
  • **<font style="color:rgb(64, 64, 64);">tmp < 0</font>**
    • 表示有等待线程(可能是读者或写者)。
  • **<font style="color:rgb(0, 0, 0);">unlikely(...)</font>**
    • 这是一个编译器提示,表示括号内的条件不常为真。
  • **<font style="color:rgb(64, 64, 64);">rwsem_wake(sem)</font>**
    • 用于唤醒等待队列中等待锁的进程。当写锁释放后,如果有读者或写者在等待队列中等待,它们会被唤醒,重新尝试获取锁。
    • 如果有多个进程在等待,它们会被按顺序唤醒,直到锁被重新获取。

读写信号量的优化

linux 4.12中读写信号量的实现结合了以下机制:

  • 乐观自旋:通过 <font style="color:rgb(0, 0, 0);">osq</font> 实现自旋锁机制,减少锁的获取延迟。
  • 优先级继承:解决优先级反转问题。
  • 公平锁:利用<font style="color:rgb(0, 0, 0);">wait_list</font>等待队列实现,按先进先出(FIFO)唤醒等待队列中任务的时候。
    • 可能会有写者饥饿的情况。
    • linux 4.12中没有写着优先或者读者优先机制。
    • 可以根据实际使用场景,设计写者优先或者读写优先的机制。

乐观自旋机制

乐观自旋是一种优化机制,用于减少线程睡眠和唤醒的开销。其核心思想是:

  • 如果锁的持有者正在运行,当前线程可能会自旋等待,而不是立即睡眠。
  • 自旋的时间是有限的,如果超过一定时间仍未获取锁,线程会进入睡眠状态。

<font style="color:rgb(64, 64, 64);">struct rw_semaphore</font> 中,乐观自旋通过 <font style="color:rgb(64, 64, 64);">osq</font>(MCS 锁)和 <font style="color:rgb(64, 64, 64);">owner</font> 字段实现:

  • <font style="color:rgb(64, 64, 64);">osq</font> 用于管理自旋线程的队列。
  • <font style="color:rgb(64, 64, 64);">owner</font> 用于判断锁的持有者是否正在运行。

优先级继承机制

优先级继承用于解决优先级反转问题。其核心思想是:

  • 当高优先级线程因低优先级线程持有锁而阻塞时,低优先级线程会临时继承高优先级线程的优先级。
  • <font style="color:rgb(64, 64, 64);">struct rw_semaphore</font> 中,通过 <font style="color:rgb(64, 64, 64);">owner</font> 字段记录写锁的持有者,支持优先级继承。

读写信号量的使用场景

读写信号量适用于以下场景:

  • 多个读者同时读取共享资源,但只有一个写者可以修改资源。
  • 读操作频繁,写操作较少的场景。

例如:

  • 文件系统的元数据操作(读多写少)。
  • 网络配置的读写操作。

用户空间中的读写信号量

在用户空间中,POSIX线程库(pthread)提供了读写锁(read-write lock),功能类似于内核中的读写信号量。也是通过阻塞方式实现的。

读写锁的基本操作

初始化

pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
pthread_rwlock_init(&rwlock, NULL);

读操作

pthread_rwlock_rdlock(&rwlock);
pthread_rwlock_unlock(&rwlock);

写操作

pthread_rwlock_wrlock(&rwlock);
pthread_rwlock_unlock(&rwlock);

尝试获取锁

pthread_rwlock_tryrdlock(&rwlock);
pthread_rwlock_trywrlock(&rwlock);

销毁

pthread_rwlock_destroy(&rwlock);

Sample

以下是一个简单的示例,展示了如何在用户空间中使用读写锁:

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;

void* reader(void* arg) {
    pthread_rwlock_rdlock(&rwlock);
    printf("Reader %ld: Reading...\n", (long)arg);
    // 模拟读取操作
    sleep(1);
    pthread_rwlock_unlock(&rwlock);
    return NULL;
}

void* writer(void* arg) {
    pthread_rwlock_wrlock(&rwlock);
    printf("Writer %ld: Writing...\n", (long)arg);
    // 模拟写入操作
    sleep(1);
    pthread_rwlock_unlock(&rwlock);
    return NULL;
}

int main() {
    pthread_t threads[3];

    // 创建读线程
    for (long i = 0; i < 2; i++) {
        pthread_create(&threads[i], NULL, reader, (void*)i);
    }

    // 创建写线程
    pthread_create(&threads[2], NULL, writer, (void*)2);

    // 等待线程结束
    for (int i = 0; i < 3; i++) {
        pthread_join(threads[i], NULL);
    }

    pthread_rwlock_destroy(&rwlock);
    return 0;
}

总结

  • 内核中的读写信号量 提供了一个高效的同步机制,允许多个读者同时访问共享资源,但写者需要独占访问。
  • 用户空间中的读写锁 提供了类似的功能,通过POSIX线程库可以实现读写锁,用于同步多线程访问共享资源。

参考资料

  1. Professional Linux Kernel Architecture,Wolfgang Mauerer
  2. Linux内核深度解析,余华兵
  3. Linux设备驱动开发详解,宋宝华
  4. linux kernel 4.12
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值