信号量在内核中的定义如下:

struct semaphore {
	raw_spinlock_t		lock;///自旋锁
	unsigned int		count;///count=1时可进行互斥操作
	struct list_head	wait_list;
};

信号量的初始化:

sem_init(&sem,val);///var代表信号量的初始值

获取信号量:

down(&sem);若此时信号量为0,则该进程会会处于睡眠状态,因此该函数不可用于中断上下文中。

接下来分析一下获取信号量的源码:

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 task_struct *task = current;
	struct semaphore_waiter waiter;

	list_add_tail(&waiter.list, &sem->wait_list);
	waiter.task = task;
	waiter.up = 0;
///死循环
	for (;;) {
	///如果当前进程被信号唤醒,则退出
		if (signal_pending_state(state, task))
			goto interrupted;
	///如果进程的等待时间超时,则退出
		if (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);
		///如果调度是由信号量的释放而唤醒的,则返回0
		if (waiter.up)
			return 0;
	}

  ......
}

释放信号量

up(&sem);

互斥信号量:

struct mutex {
	/* 1: unlocked, 0: locked, negative: locked, possible waiters */
	atomic_t		count;
	spinlock_t		wait_lock;
	struct list_head	wait_list;
 ......
};

互斥信号量的初始化:

init_mutex(&sem);

同样作为同步操作,mutex、spinlock、semaphore有如下差异:

1、mutex的count初始化为1,而semaphore则初始化为0

2、mutex的使用者必须为同一线程,即必须成对使用,而semaphore可以由不同的线程执行P.V操作。

3、进程在获取不到信号量的时候执行的是sleep操作,而进程在获取不到自旋锁的时候执行的是忙等待操作。因此,不难看出,如果需要保护的临界区比较小,锁的持有时间比较短的情况下,通常使用spinlock。这样可以不需要对等待锁的进程执行睡眠/唤醒操作,大大节省了cpu时间。因此,spinlock通常作为多处理器之间的同步操作。