如果一个任务获取信号量失败,该任务必须等到其他任务释放信号量。本文的重点是,在Linux中,当一个任务释放一个信号量时,如何唤醒一个正在等待信号量的任务。
信号量定义如下:
struct semaphore {
raw_spinlock_t lock;
unsigned int count;
struct list_head wait_list;
};
链表用于管理由于信号量获取不成功而处于休眠状态的任务。
任务尝试通过调用 down() 函数来获取信号量。如果信号量获取失败,则调用()函数。 () 函数在函数内部被调用。 (其实down()函数有多种变体,比如信号量获取失败时,也会调用该函数。不同的down()函数最终调用不同的参数,处理不同的信号量获取方式。正在发生)。
同时整个down()函数被sem->lock保护。
void down(struct semaphore *sem)
{
unsigned long flags;
raw_spin_lock_irqsave(&sem->lock, flags);
if (likely(sem->count > 0))
sem->count--;
else
__down(sem);
raw_spin_unlock_irqrestore(&sem->lock, flags);
}
static noinline void __sched __down(struct semaphore *sem)
{
__down_common(sem, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
}
这里是重点:函数如何休眠任务,休眠任务如何被唤醒并获取信号量。
是一个关键数据结构,表示未能获取信号量的等待任务。 up字段标识任务是否被信号量唤醒,即休眠任务被某个信号唤醒后,判断是否被等待信号量唤醒。
struct semaphore_waiter {
struct list_head list;
struct task_struct *task;
bool up;
};
函数首先初始化一个。 task字段标识当前任务,up设置为false。
static inline int __sched __down_common(struct semaphore *sem, long state,
long timeout)
{
struct semaphore_waiter waiter;
list_add_tail(&waiter.list, &sem->wait_list);
waiter.task = current;
waiter.up = false;
...
然后休眠当前任务并调用 () 主动让出 CPU。上面说了,整个函数都在sem->lock的临界区,但是在spin lock的临界区是不可能sleep的,所以实际上在sleep之前就释放了锁,重新加锁被唤醒后获得。 .
当任务被唤醒时,如果.up为真,则任务可以获得信号量。 .up必须要判断,根据()函数传入的参数,任务可能处于不同的睡眠状态,可能被不同的信号唤醒,不一定是等待信号。
for (;;) {
if (signal_pending_state(state, current))
goto interrupted;
if (unlikely(timeout lock);
timeout = schedule_timeout(timeout);
raw_spin_lock_irq(&sem->lock);
if (waiter.up)
return 0;
}
timed_out:
list_del(&waiter.list);
return -ETIME;
interrupted:
list_del(&waiter.list);
return -EINTR;
}
当一个任务释放信号量时,如果该信号量的等待队列中有任务,则将队列中第一个任务的up标记为真,将其唤醒,同时将其从等待队列中删除时间。
同时,sem->count只在等待队列为空时才更新,保证等待队列中的任务优先于新任务获取信号量,严格的先进先出是保证。等待队列中的任务因为有新任务而被饿死。
void up(struct semaphore *sem)
{
unsigned long flags;
raw_spin_lock_irqsave(&sem->lock, flags);
if (likely(list_empty(&sem->wait_list)))
sem->count++;
else
__up(sem);
raw_spin_unlock_irqrestore(&sem->lock, flags);
}
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);
}
任务被唤醒后,检测到up为真,返回0,成功获取信号量。
暂无评论内容