信号量在内核中的定义如下:
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通常作为多处理器之间的同步操作。