一、介绍
信号量用于内核驱动长时间占用的只用于保护大块临界区;
如果试图获取信号量的进程获取失败,内核就会将其调度为睡眠状态,执行其他进程,避免了CPU的忙等。
信号量和自旋锁对比
项目 | 信号量 | 自旋锁 |
---|---|---|
临界区时间 | 进程切换时间更短 | 临界区执行时间更短 |
进程上下文 | 临界区可以睡眠或调度 | 临界区不可以睡眠或调度 |
中断上下文 | 只有down_trylock()可以 | 可以 |
二、函数使用
传统信号量
用于对临界资源的互斥/顺序访问
//include/linux/semaphore.h
//定义并初始化semaphore对象
struct semphore sem;
//初始化信号量
void sem_init(struct semaphore * sem,int val);
init_MUTEX(sem);
init_MUTEX_LOCKED(sem);
DECLARE_MUTEX(sem);
DECLARE_MUTEX_LOCKED(sem);
//P操作
//down()会导致睡眠,不能用于中断上下文
void down(struct semaphore *sem);
//down_interruptible同样会进入休眠,但能被打断
int down_interruptible(struct semaphore *sem);
//down_trylock不能获得锁时会立即返回,不会睡眠,可以用在中断上下文
int down_trylock(struct semaphore *sem);
//V操作
void up(struct semaphore *sem);
读写信号量
读写信号量与信号量的关系 和 读写自旋锁与自旋锁的关系类似,允许多个CPU同时读临界资源,但不允许同时写资源
//定义并初始化读写信号量
struct rw_semaphore my_rwsem;
void init_rwsem(struct rw_semaphore *sem);
//P读信号量
void down_read(struct rw_semaphore *sem);
int down_read_trylock(struct rw_semaphore *sem);
//V读信号量
void up_read(struct rw_semaphore *sem);
//P写信号量
void down_write(struct rw_semaphore *sem);
int down_write_trylock(struct rw_semaphore *sem);
//V写信号量
void up_write(struct rw_semaphore *sem);
示例
struct rw_semaphore my_rwsem;
void init_rwsem(&my_rwsem);
//读前获取读信号量
down_read(&my_rwsem); //若要非阻塞:down_read_trylock(&my_rwsem);
/* 读临界区 */
//读完释放读信号量
up_read(&my_rwsem);
//写前获取写信号量
down_write(&my_rwsem); //若要非阻塞:down_write_trylock(&my_rwsem);
/* 写临界区 */
//写完释放写信号量
up_write(&my_rwsem);
完成量
完成量用于一个执行单元等待另一个执行单元执行完某事,和传统信号量一样,主要是用来实现队临界区的顺序/互斥访问。但是完成量还提供一种唤醒一个或唤醒所有等待进程的接口,有点类似与应用层的条件变量
//定义并初始化完成量
struct completion my_completion;
init_completion(&my_completion);
//或
DECLARE_COMPLETION(my_completion)
//等待completion
void wait_for_completion(struct completion *c);
//唤醒completion
void complete(struct completion *c); //只唤醒一个等待的执行单元
void complete_all(struct completion *c); //释放所有等待该完成量的执行单元