信号量和互斥体
- 几个相关概念
- 休眠:当一个linux进程到达某个时间点,此时它不能进行任何处理时,它将进入休眠(或阻塞),这将把处理器让给其它进程,知道将来它能够继续完成自己的处理为止。
- 为了让临界区正确工作,我们选择使用的锁定原语必须在其它拥有这个锁并休眠的情况下工作。
- 信号量: 实质上是一个整数, 与 P 和 V 函数联合使用
- 进入临界区, 调用 P 函数
- 信号量 > 0: 进程可以继续, 信号量减一
- 信号量 <=0: 进程等待, 直到其他人释放信号量
- 释放信号量调用 V 函数, 释放信号量, 增加其值, 必要时唤醒等待进程
- 进入临界区, 调用 P 函数
- 互斥体: 此时信号量初始化为 1, 可避免多个进程同时在临界区, 此时信号量称为互斥体(mutex, 全称是 mutual exclusion)
- Linux 信号量的实现
- 实现代码及结构类型
- <asm/semaphore.h>
- struct semaphore
- 声明和初始化
- 直接创建信号量: void sema_init(struct semaphore *sem, int val);
- val 是对信号量赋予的初始值
- 互斥模式声明
- DECLARE_MUTEX(name);
- 初始化信号量 name 为 1
- + - DECLARE_MUTEX_LOCKED(name);
- 初始化信号量 name 为 0, 为锁定状态, 使用时需要解锁
- DECLARE_MUTEX(name);
- 互斥体的动态初始化
- void init_MUTEX(struct semaphore *sem);
- void init_MUTEX_LOCKED(struct semaphore *sem);
- 直接创建信号量: void sema_init(struct semaphore *sem, int val);
- P 函数实现 (down 函数)
- 功能: 减小信号量的值, 可能会使进程休眠, 等待直到信号量可用.
- 实现函数
- void down(struct semaphore *sem);
- 减小信号量, 在必要时会一直等待
- int down_interruptible(struct semaphore *sem);
- 与上述函数功能相同, 但可以中断
- int down_trylock(struct semaphore *sem);
- 进程永不会休眠, 如果信号量不可获得, 函数返回非零值
- void down(struct semaphore *sem);
- V 函数实现 (up 函数)
- void up(struct semaphore *sem);
- 释放信号量
- void up(struct semaphore *sem);
- 注意: 在拥有一个信号量时发生错误, 需要在释放信号量前返回错误, 否则将无法返回
- 实现代码及结构类型
- 读取者/写入者信号量
- 功能说明: 可以允许多个并发读取, 但只能单一写入
- 信号量 rwsem
- 代码路径: <linux/rwsem.h>
- 结构: struct rw_semaphore;
- 声明和初始化
- void init_rwsem(struct rw_semaphore *sem);
- rwsem 只读访问
- void down_read(struct rw_semaphore *sem);
- 对受保护资源的只读访问, 可和其它读取者并发访问.
- 问题: 可能将进程置于不可中断的休眠
- int down_read_trylock(struct rw_semaphore *sem);
- 访问不可获得时不等待
- 访问成功返回非零值, 访问失败返回零值.
- void up_read(struct rw_semaphore *sem);
- 释放信号量
- void down_read(struct rw_semaphore *sem);
- rwsem 写入访问
- 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(strut rw_semaphore *sem);
- 当某个快速改变获得了写入者锁, 其后是更长时间的读取,
此时可以调用该函数, 允许其它读取者访问.
- 当某个快速改变获得了写入者锁, 其后是更长时间的读取,
- 问题及对策
- 当有多个写入者竞争该信号, 会导致读取者 "饿死"
- 在很少写入或者写入者只短期拥有信号时使用
- completion
- 功能及信号量方式代码实现
-
在当前进程之外初始化某个活动, 然后等待该活动的结束, 这样可以同步两个任务
struct semaphore sem;
init_MUTEX_LOCKED(&sem);
start_external_task(&sem);
down(&sem);
......(执行任务)
up(&sem);
-
- 介绍及代码实现
- completion 是一种轻量级的机制, 它允许一个线程告诉另一个线程某个工作已完成
- <linux/compltion.h>
- 定义及初始化
-
静态初始化: DECLARE_COMPLETION(my_completion);
动态初始化:
struct conpletion my_semaphore;
init_completion(&my_semaphore);
-
- 函数使用
- void wait_for_completion(struct completion *c);
-
这是一个非中断调用, 如果进程用该函数却没有人来完成任务,
则会出现一个不可杀死进程
-
- void complete(struct completion *c);
- 会唤醒一个等待completion的进程
- void complete_all(struct completion *c);
- 会唤醒所有等待completion的进程
- void wait_for_completion(struct completion *c);
- 注意
- 一个 completion 是一个单次(one-shot)设备;它只会被使用一次, 然后丢弃
- 仔细处理后可以重复使用
-
使用 complete(struct completion *)时可以重复使用同一个 completion 结构, 只要那个将要出发的时间是明确的, 不是含糊的.
使用complete_all(struct completion *)时, 要想重复使用同一个 completion, 需要重新初始化, 用下面的宏: INIT_COMPLETION(struct completion c);
-
- completion 的典型使用
-
模块退出时的内核线程终止;
某些驱动程序的内部工作由一个内核线程在 while(1) 循环中完成.
当内核准备清除该模块时, exit函数会告诉线程退出并等待 completion, 下面函数实现该功能
vonid complete_and exit(struct competion *c, long retval);
-
- 功能及信号量方式代码实现