In continuation of the previous text第5章:并发与竞态条件-4:Using Semaphores in scull, let's GO ahead.
Reader/Writer Semaphores
Semaphores perform mutual exclusion for all callers, regardless of what each thread may want to do. Many tasks break down into two distinct types of work, however: tasks that only need to read the protected data structures and those that must make changes. It is often possible to allow multiple concurrent readers, as long as nobody is trying to make any changes. Doing so can optimize performance significantly; read-only tasks can get their work done in parallel without having to wait for other readers to exit the critical section.
The Linux kernel provides a special type of semaphore called a rwsem (or “reader/writer semaphore”) for this situation. The use of rwsems in drivers is relatively rare, but they are occasionally useful.
普通信号量会对所有调用者进行互斥限制,无论线程要执行的操作类型。但许多任务可分为两类:仅需读取受保护数据结构的任务,和必须修改数据的任务。通常情况下,允许多个读线程并发访问是安全的 —— 只要没有写线程在执行修改操作。这种设计能显著优化性能:只读任务无需等待其他读线程退出临界区,可并行完成工作。
Linux 内核为此场景提供了一种特殊的信号量,称为 rwsem(读写信号量)。驱动中使用 rwsem 的场景相对少见,但在特定情况下非常实用。
Code using rwsems must include <linux/rwsem.h>. The relevant data type for reader/writer semaphores is struct rw_semaphore; an rwsem must be explicitly initialized at runtime with
使用 rwsem 的代码必须包含 <linux/rwsem.h> 头文件。读写信号量的相关类型是 struct rw_semaphore,必须在运行时通过以下函数显式初始化:
void init_rwsem(struct rw_semaphore *sem);
A newly initialized rwsem is available for the next task(reader or writer) that comes along. The interface for code needing read-only access is:
新初始化的 rwsem 处于可用状态,可被后续的读线程或写线程获取。需要只读访问受保护资源的线程,使用以下接口:
void down_read(struct rw_semaphore *sem);
int down_read_trylock(struct rw_semaphore *sem);
void up_read(struct rw_semaphore *sem);
A call to down_read provides read-only access to the protected resources, possibly concurrently with other readers. Note that down_read may put the calling process into an uninterruptible sleep. down_read_trylock will not wait if read access is unavailable; it returns nonzero if access was granted, 0 otherwise. Note that the convention for down_read_trylock differs from that of most kernel functions, where success is indicated by a return value of 0. A rwsem obtained with down_read must eventually be freed with up_read.
-
down_read:获取读锁,允许与其他读线程并发访问。该函数可能让调用进程进入不可中断睡眠。 -
down_read_trylock:尝试获取读锁,不会等待。若获取成功返回非零值,失败返回 0(注意:这与内核多数函数 “返回 0 表示成功” 的惯例不同)。 -
up_read:释放读锁,必须与down_read或成功的down_read_trylock成对调用。
The interface for writers is similar:
需要修改受保护资源的线程,使用以下接口:
void down_write(struct rw_semaphore *sem);
int down_write_trylock(struct rw_semaphore *sem);
void up_write(struct rw_semaphore *sem);
void downgrade_write(struct rw_semaphore *sem);
down_write, down_write_trylock, and up_write all behave just like their reader counterparts, except, of course, that they provide write access. If you have a situation where a writer lockis needed for a quickchange, followed by a longer period of read only access, you can use downgrade_write to allow other readers in once you have finished making changes.
-
down_write:获取写锁,排他性访问(禁止其他读线程和写线程同时访问),可能进入不可中断睡眠。 -
down_write_trylock:尝试获取写锁,不会等待。成功返回非零值,失败返回 0。 -
up_write:释放写锁,需与down_write或成功的down_write_trylock成对调用。 -
downgrade_write:将写锁降级为读锁。适用于 “先快速修改数据,后长时间只读访问” 的场景 —— 降级后允许其他读线程进入,提升并发性能。
An rwsem allows either one writer or an unlimited number of readers to hold the semaphore. Writers get priority; as soon as a writer tries to enter the critical section, no readers will be allowed in until all writers have completed their work. This implementation can lead to reader starvation—where readers are denied access for a long time—if you have a large number of writers contending for the semaphore. For this reason, rwsems are best used when write access is required only rarely, and writer access is held for short periods of time.
-
访问规则:同一时刻,rwsem 要么被一个写线程持有,要么被任意数量的读线程持有(不可同时存在读和写线程)。
-
写线程优先级:一旦有写线程尝试获取锁,后续读线程会被阻塞,直到所有写线程完成操作并释放锁。
-
潜在问题:若写线程竞争激烈,可能导致读线程饥饿(长时间无法获取锁)。
rwsem 最适合以下情况:
-
读操作的频率远高于写操作;
-
写操作持续时间短,且不频繁。
若写操作频繁或持续时间长,rwsem 的并发优势会消失,甚至因 “写优先级” 导致读线程饥饿,此时不如使用普通信号量(或互斥锁)更高效。
补充说明:
-
不可中断睡眠的限制
down_read和down_write均为不可中断操作,适合内核态中无需响应信号的场景(如中断处理程序之外的同步)。用户态进程若需可中断等待,需自行封装(或避免使用 rwsem)。 -
锁降级的注意事项
downgrade_write只能将写锁降级为读锁,无法反向升级(读锁不能升级为写锁)。降级后,当前线程仍持有读锁,其他读线程可并发访问,但写线程需等待所有读锁释放。 -
与普通信号量的对比
-
普通信号量:无论读写,均排他访问,实现简单,无饥饿问题;
-
rwsem:读并发、写排他,读多写少场景下性能更优,但实现复杂,可能出现读饥饿。
-
-
现代内核的替代方案
现代内核提供了
struct rw_semaphore的改进版本(如struct seqlock、struct rwlock_t),针对不同场景优化了性能和灵活性,可根据实际需求选择。
291

被折叠的 条评论
为什么被折叠?



