本篇文章基于Linux-6.5源码
建议:搭配Linux源码观看更佳
struct semaphore {
raw_spinlock_t lock; // 保护信号量的自旋锁
unsigned int count; // 最大同时可访问临界区的进程数量
struct list_head wait_list; // 等待队列,wait_list指向队列末尾
};
struct semaphore_waiter {
struct list_head list; //表示当前任务在等待队列中的节点指针
struct task_struct *task; //
bool up; //任务等待状态,true或false
};
API接口
sema_init
#define __SEMAPHORE_INITIALIZER(name, n) \
{ \
.lock = __RAW_SPIN_LOCK_UNLOCKED((name).lock), \
.count = n, \
.wait_list = LIST_HEAD_INIT((name).wait_list), \
}
//初始化信号量sem,sem->count = val。
static inline void sema_init(struct semaphore *sem, int val)
{
static struct lock_class_key __key;
*sem = (struct semaphore) __SEMAPHORE_INITIALIZER(*sem, val);
lockdep_init_map(&sem->lock.dep_map, "semaphore->lock", &__key, 0);
}
down
获取信号量,获取成功,则sem->count -1。获取失败,将进程加入等待队列,开始休眠,直到已获得信号量的进程释放信号量时,会唤醒等待队列的进程。进程睡眠时,状态是TASK_UNINTERRUPTIBLE,表示睡眠过程不接受信号打断,
void __sched down(struct semaphore *sem)
{
unsigned long flags;
might_sleep();
//加锁,将count操作
raw_spin_lock_irqsave(&sem->lock, flags);
if (likely(sem->count > 0))
sem->count--;
else
//如果count已为0,则获取失败
__down(sem);
raw_spin_unlock_irqrestore(&sem->lock, flags);
}
EXPORT_SYMBOL(down);
static noinline void __sched __down(struct semaphore *sem)
{
__down_common(sem, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
}
static inline int __sched ___down_common(struct semaphore *sem, long state,
long timeout)
{
struct semaphore_waiter waiter;
//把当前进程加入到信号量sem的等待队列wait_list中
list_add_tail(&waiter.list, &sem->wait_list);
waiter.task = current; //记录当前任务的task_struct
waiter.up = false; //false表示在等待
//循环
for (;;) {
if (signal_pending_state(state, current))
goto interrupted;
if (unlikely(timeout <= 0))
goto timed_out;
__set_current_state(state); //设置当前进程状态为不可中断状态
raw_spin_unlock_irq(&sem->lock);
timeout = schedule_timeout(timeout); //主动调度,等待timeout或者被wake_up
raw_spin_lock_irq(&sem->lock);
//当已获得信号量的进程释放时,会唤醒等待队列的进程,此时up为ture,跳出循环
if (waiter.up)
return 0;
}
timed_out:
list_del(&waiter.list);
return -ETIME;
interrupted:
list_del(&waiter.list);
return -EINTR;
}
up
释放信号量:如果等待队列为空,说明没有任务在等待,则直接将count++。如果不为空,唤醒队列头部的任务,并出队。
void __sched up(struct semaphore *sem)
{
unsigned long flags;
raw_spin_lock_irqsave(&sem->lock, flags);
//如果等待队列为空,说明没有任务在等待,则直接将count++
if (likely(list_empty(&sem->wait_list)))
sem->count++;
else
//否则,出队等待队列中的任务,并唤醒
__up(sem);
raw_spin_unlock_irqrestore(&sem->lock, flags);
}
EXPORT_SYMBOL(up);
static noinline void __sched __up(struct semaphore *sem)
{
//获得队列头的任务
struct semaphore_waiter *waiter = list_first_entry(&sem->wait_list,
struct semaphore_waiter, list);
list_del(&waiter->list); //出队
waiter->up = true; //表示不再等待
wake_up_process(waiter->task); //唤醒任务
}