1. 读取/写入信号量
信号量对所有调用者执行互斥操作, 而不管线程想做什么(读/写). 正如这样, 我们可以把任务划分为两种类型: 读取和写入. 多个并发的读取应该是被允许的, 因为只读任务可并行完成他们的工作, 这样做可以大大提高性能.
如此, 便有了Linux内核提供的一种特殊信号量类型, 称为"rwsem"(reader/writer semaphore). 虽然在驱动程序中使用rwsem的机会相对比较少, 但偶尔也比较有用.
头文件: <linux/rwsem.h>
数据类型: struct rw_semaphore
一个rwsem对象必须用一下函数显式地初始化:
void init_rwsem(struct rw_semaphore *sem);
对受保护资源的只读访问, 可和其他读取者并发地访问. 可用的接口有:
/* 可能会将调用进程置于不可中断的休眠 */
void down_read(struct rw_semaphore *sem);
/* 可中断版本
* 授予访问时返回非0, 其他情况返回0 */
int down_read_trylock(struct rw_semaphore *sem);
/* 释放rw_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);
这3个函数与读取者的对应函数行为相同, 他们提供的是写入访问.
/* 当某个快速改变获得写入者锁,
*其后有更长时间的只读访问的时候,
*我们可以调用该函数来允许其他读取者访问 */
void downgrade_write(struct rw_semaphore *sem);
读取/写入信号量特点:
2. Completion
有时, 由于信号量轻松up操作之后, 会产生过长时间的操作, 为了避免信号量执行down操作导致的长时间阻塞, 内核提供了一组completion(完成)接口解决这种问题.
Completion是一种轻量级的机制, 它允许一个线程告诉另一个线程某个工作已经完成.
头文件: <linux/completion.h>
数据类型: struct completion
一个completion对象可以通过两种方法创建和初始化:
DECLARE_COMPLETION(my_completion);
或者, 如果必须动态, 则使用:
struct completion my_completion;
...
...
init_completion(&my_completion);
对completion操作的接口有:
等待completion的操作可以用:
/* 执行一个非中断等待 */
void wait_for_completion(struct completion *c);
实际的completion事件调用可以用:
/* 只会唤醒一个等待线程 */
void complete(struct completion *c);
/* 唤醒所有等待线程 */
void complete_all(struct completion *c);
但在大多数情况下, 只会有一个等待者, 此时两个函数效果相同.
Completion的特点:
Completion机制的典型是模块退出时的内核线程终止. 这时, 驱动程序内部由一个while(1)循环完成, 当内核准备清楚该模块时, exit函数会令线程退出并等待completion. 此时, 内核可调用一个特殊函数:
void complete_and_exit(struct completion *c, long retval);
/* 其中retval是exit的返回值, 可用于错误代码检查 */
信号量对所有调用者执行互斥操作, 而不管线程想做什么(读/写). 正如这样, 我们可以把任务划分为两种类型: 读取和写入. 多个并发的读取应该是被允许的, 因为只读任务可并行完成他们的工作, 这样做可以大大提高性能.
如此, 便有了Linux内核提供的一种特殊信号量类型, 称为"rwsem"(reader/writer semaphore). 虽然在驱动程序中使用rwsem的机会相对比较少, 但偶尔也比较有用.
头文件: <linux/rwsem.h>
数据类型: struct rw_semaphore
一个rwsem对象必须用一下函数显式地初始化:
void init_rwsem(struct rw_semaphore *sem);
对受保护资源的只读访问, 可和其他读取者并发地访问. 可用的接口有:
/* 可能会将调用进程置于不可中断的休眠 */
void down_read(struct rw_semaphore *sem);
/* 可中断版本
* 授予访问时返回非0, 其他情况返回0 */
int down_read_trylock(struct rw_semaphore *sem);
/* 释放rw_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);
这3个函数与读取者的对应函数行为相同, 他们提供的是写入访问.
/* 当某个快速改变获得写入者锁,
*其后有更长时间的只读访问的时候,
*我们可以调用该函数来允许其他读取者访问 */
void downgrade_write(struct rw_semaphore *sem);
读取/写入信号量特点:
- 一个rwsem可以允许一个写入者或无限多个读取者拥有该信号量.
- 写入者具有更高的优先级, 某个写入者试图进入临界区, 在其完成工作之前, 不会允许读取者获得访问.
- 如果大量写入者竞争该信号量, 会导致读取者"饿死".
- 最好在很少需要写访问且写入者只会短期拥有信号量什使用rwsem.
2. Completion
有时, 由于信号量轻松up操作之后, 会产生过长时间的操作, 为了避免信号量执行down操作导致的长时间阻塞, 内核提供了一组completion(完成)接口解决这种问题.
Completion是一种轻量级的机制, 它允许一个线程告诉另一个线程某个工作已经完成.
头文件: <linux/completion.h>
数据类型: struct completion
一个completion对象可以通过两种方法创建和初始化:
DECLARE_COMPLETION(my_completion);
或者, 如果必须动态, 则使用:
struct completion my_completion;
...
...
init_completion(&my_completion);
对completion操作的接口有:
等待completion的操作可以用:
/* 执行一个非中断等待 */
void wait_for_completion(struct completion *c);
实际的completion事件调用可以用:
/* 只会唤醒一个等待线程 */
void complete(struct completion *c);
/* 唤醒所有等待线程 */
void complete_all(struct completion *c);
但在大多数情况下, 只会有一个等待者, 此时两个函数效果相同.
Completion的特点:
- 一个completion通常是一个单次设备, 它只会被使用一次然后被丢弃.
- 触发事件明确时, 如果没有使用complete_all, 可以重复使用一个completion结构.
- 如果使用了complete_all, 则必须在重复使用该结构之前重新初始化它:
- 可以用INIT_COMPLETION(struct completion *c);进行初始化
Completion机制的典型是模块退出时的内核线程终止. 这时, 驱动程序内部由一个while(1)循环完成, 当内核准备清楚该模块时, exit函数会令线程退出并等待completion. 此时, 内核可调用一个特殊函数:
void complete_and_exit(struct completion *c, long retval);
/* 其中retval是exit的返回值, 可用于错误代码检查 */