信号量的实现
- 软件平台:运行于VMware Workstation 12 Player下UbuntuLTS16.04_x64 系统
- 开发环境:Linux 3.4.2内核、arm-linux-gcc 4.3.2工具链
目录
4、信号量semaphore的实现
4.1 信号signal与信号量semaphore
信号(signal) | 信号量(semaphore) | |
---|---|---|
使用场景 | 处理异步事件的方式 | 处理同步互斥的机制 |
作用 | 由用户、系统或者进程发送给目标进程的信息,以通知目标进程某个状态的改变或系统异常 | 调协进程对共享资源的访问,让一个临界区同一时间只有一个进程在访问它 |
4.2 信号量semaphore的内核数据结构
其数据结构为struct semaphore
结构体,定义在include\linux\semaphore.h
文件中:
struct semaphore {
raw_spinlock_t lock; /* 借助spinlock */
unsigned int count; /* 计数值,能同时允许访问的数量 */
struct list_head wait_list; /* 等待队列 */
};
与spinlock相比,信号量semaphore会导致进程休眠,开销较大,适用于保护较长的临界区;
4.3 down()函数实现
获得信号量的函数down()
,其实现在kernel\locking\semaphore.c
文件中:
- 需要借用 spinlock;
- 判读锁的数量
sem->count
,进行相应的获得锁或休眠操作;
void down(struct semaphore *sem)
{
unsigned long flags;
raw_spin_lock_irqsave(&sem->lock, flags); /* 1、借用spinlock */
if (likely(sem->count > 0)) /* 2.1 锁的数量 > 0,成功获得信号量,sem->count-- */
sem->count--;
else /* 2.2 锁的数量不足,无法获得信号量,休眠 */
__down(sem);
raw_spin_unlock_irqrestore(&sem->lock, flags);
}
当锁的数量不足时,进行休眠操作:
- 把进程号放入等待队列中
sem->wait_list
; - 设置唤醒标志位为false;
- 设置进程的状态为:**(
TASK_UNINTERRUPTIBLE
)**处于等待队列中 且 不可被中断唤醒; - 解除spinlock,否则其他进程无法释放信号量;
- 进行睡眠,直至timeout;
- 加spinlock锁;
- 判断唤醒标志位是否为true,是则停止睡眠,否则重新进入循环——睡眠。
4.4 up()函数的实现
释放信号量的函数up()
,其实现在kernel\locking\semaphore.c
文件中:
- 需要借用 spinlock;
- 判读等待队列是否为空
sem->lock
,进行相应的释放锁或唤醒操作;
void up(struct semaphore *sem)
{
unsigned long flags;
raw_spin_lock_irqsave(&sem->lock, flags); /* 1、借助spinlock */
if (likely(list_empty(&sem->wait_list))) /* 2.1 等待队列为空,增加锁sem->count++ */
sem->count++;
else
__up(sem); /* 2.2 等待队列不为空,唤醒等待队列的第一个进程 */
raw_spin_unlock_irqrestore(&sem->lock, flags);
}
EXPORT_SYMBOL(up);
当等待队列不为空时,则进行唤醒等待队列的第一个进程:
- 从等待队列中获取第一个进程;
- 从等待队列中删除该进程;
- 设置唤醒标志位为true;
- 唤醒该进程;