申请信号量的函数有
1.void down(struct semaphore *sem)
2.int down_interruptible(struct semaphore *sem)
3.int down_killable(struct semaphore *sem)
4.int down_trylock(struct semaphore *sem)
5.int down_timeout(struct semaphore *sem, long timeout)
先跟第一个函数down()
code:
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);
}
EXPORT_SYMBOL(down);
这里主要看下raw_spin_lock_irqsave 和down函数
static inline unsigned long __raw_spin_lock_irqsave(raw_spinlock_t *lock)
{
unsigned long flags;local_irq_save(flags);
preempt_disable();
spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
/*
* On lockdep we dont want the hand-coded irq-enable of
* do_raw_spin_lock_flags() code, because lockdep assumes
* that interrupts are not re-enabled during lock-acquire:
*/
#ifdef CONFIG_LOCKDEP
LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);
#else
do_raw_spin_lock_flags(lock, &flags);
#endif
return flags;
}
这里主要干了3个事,一个是保存中断状态 一个是禁止内核抢占,再一个就是获取锁
看到网上说这个函数也会关闭中断,但是没看到相应的关中断的操作,这个有点不明白,不知道是不是关中断的操作再local_irq_save()里已经做了,这是个疑问,也就是说raw_spin_lock_irqsave: 保存进入临界区前的中断状态,关闭本地中断,获取锁。
再继续看do_raw_spin_lock_flags这个函数
252 void do_raw_spin_lock(raw_spinlock_t *lock)
253 {
254 debug_spin_lock_before(lock);
255 if (unlikely(!))
256 __spin_lock_debug(lock);
257 debug_spin_lock_after(lock);
258 }
最终跑到了这里arch_spin_trylock(&lock->raw_lock)
再回头看看 __down(sem);
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;
}
加粗的地方,设置当前线程状态为TASK_UNINTERRUPTIBLE,然后释放锁,因为后面试图获取信号量的进程申请不到的时候,会进入休眠,所以再这里休眠之前要释放之前这里申请的锁 raw_spin_lock_irqsave(&sem->lock, flags);最后执行schedule_timeout()进行进入等待信号量,重新得到执行时会 从新获取锁raw_spin_lock_irq,主要是接着raw_spin_lock_irqrestore().
对于信号量中锁的操作,在raw_spin_lock_irqsave 和 raw_spin_unlock_irq 最后都是条用到arch_spin_lock arch_spin_unlock,也就是说再休眠等待之前,把之前的锁要给释放。
有些地方不是很明白,先记录下。