[同步机制]-信号量semaphore的代码导读和介绍

(linux/kernel/locking/semaphore.c)

down()---->__down()---->__down_common()
down_interruptible()---->__down()---->__down_common()
down_killable()---->__down_killable()---->__down_common()
down_trylock()---->__down_trylock()---->__down_common()
down_timeout()---->__down_timeout()---->__down_common()
up()---->__up()---->__up_common()

我们以down()为例进行分析

(1)、 在down()函数中,会先检查sem->count变量,如果>0表示获得该锁,直接返回,程序会继续往下跑,进入临界区; 如果<=0,则表示未获得该锁,则调用__down()进行排队和睡眠;
这里有个疑问,已知在__down()中会进入睡眠,而在spinlock的临界区是不允许睡眠的? 那么这里怎么回事呢?莫急,请继续往下看

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);
}

(2)、在__down()中,传入TASK_UNINTERRUPTIBLE标志后,直接调用__down_common

static noinline void __sched __down(struct semaphore *sem)
{
	__down_common(sem, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
}

(3)、在__down_common中,我们可以看到,首先将当前的task加入到了sem->wait_list,然后调用schedule_timeout进行睡眠.
注意在睡眠之前,调用了raw_spin_unlock_irq,释放自旋锁. 睡眠时间到了之后再调用raw_spin_lock_irq获得该锁, 这也就回答了在(1)中的疑问

static inline int __sched __down_common(struct semaphore *sem, long state,
								long timeout)
{
	struct task_struct *task = current;
	struct semaphore_waiter waiter;

	list_add_tail(&waiter.list, &sem->wait_list);
	waiter.task = task;
	waiter.up = false;

	for (;;) {
		if (signal_pending_state(state, task))
			goto interrupted;
		if (unlikely(timeout <= 0))
			goto timed_out;
		__set_task_state(task, state);
		raw_spin_unlock_irq(&sem->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;
}

总结一下 信号量semaphore:

  • down()函数,sem->count是一个计数功能,大于0表示获得该锁,则直接返回进入临界区。<=0则说明未获得该锁,调用__down(),该进程则加入到sem->wait_list,
    然后该进程调用schedule_timeout进入睡眠状态;
  • up()函数,先检查sem->wait_list,则表示没有需要唤醒的task,直接返回;
    否则的话调用__up(),先从sem->wait_list删除这个task,然后再调用wake_up_process(waiter->task),唤醒这个task;

猜你喜欢

转载自blog.csdn.net/weixin_42135087/article/details/107484695