1.信号量概述------------------------------------------------
Linux中提供了两种信号量,一种用于内核程序中,一种用于应用程序中。
由于这里讲解的是内核编程的知识,所以只对内核中的信号量进行详细讲述。
自旋锁和信号量的区别:
自旋锁:会一直检测是否可以上锁,因此当有锁可用时,自旋锁的反应快速
信号量:没有锁后会进行休眠,直到有锁后被唤醒,反应慢
注意:
从信号量的原理上来说,没有获得信号量的函数可能睡眠。这就要求只有能够睡眠的 ,进程才能够使用信号量,不能睡眠的进程不能使用信号量。例如在中断处理程序中,由于中断需要立刻完成,所以不能睡眠,也就是说在中断处理程序中是不能使用信号量的。
cout变量:
count是信号量中一个非常重要的成员变量,这个变量可能取下面的3种值。
- 如果这个值等于0,表示信号量被其他进程使用,现在不可以用这个信号量,但是wait list队列中没有进程在等待信号量。
- 如果这个值小于0,那么表示至少有一个进程在wait list队列中等待信号量被释放
- 如果这个值大于0,表示这个信号量可以用,程序可以使用这个信号量。
注意这里cout的值大于0的情况:表示一个信号量可以被多个进程使用
从这里可以看出信号量与自旋锁的一个不同是, 自旋锁只能允许一个进程持有自旋锁,而信号量可以根据count的值,设定可以有多少个进程持有这个信号量。
根据count的取值,可以将信号量分为二值信号量和计数信号量。
- 二值信号量就是count初始化时,被设置成1时的使用量,这种类型的信号量可以强制二者同一时刻只有一个运行。
- 计数信号量,其允许一个时刻有一个或者多个进程同时持有信号量。具体有多少个进,程可以持有信号量,取决于count的取值。
一般先初始化才能使用这些函数接口
static struct semaphore lock;
init_MUTEX(&lock);//初始化
#define init_MUTEX(sem) sema_init(sem, 1)
static inline void sema_init(struct semaphore *sem, int val)
{
static struct lock_class_key __key;
*sem = (struct semaphore) __SEMAPHORE_INITIALIZER(*sem, val);
lockdep_init_map(&sem->lock.dep_map, "semaphore->lock", &__key, 0);
}
重要结构体:
struct semaphore {
spinlock_t lock; //自旋锁
unsigned int count; //有几个在使用这个锁
struct list_head wait_list; //等待链表
};
虽然信号量中有自旋锁,但是这不影响信号量的本质,自旋锁作用时(当对cout操作时,保护cout不被其他进程修改)
等待队列:
wait是一个等待队列的链表头,这个链表将所有等待该信号量的进程组成一个链表结构。在这个链表中,存放了正在睡眠的进程链表。
信号量用于同步操作:
情景:在一个进程中,线程A要执行代码A,必须要等到线程B把代码B执行完后才能执行代码A
这要的要求,就需要信号量来实现
如何选择自旋锁和信号量
自旋锁是一种最简单的保护机制,从上面的代码分析中可以看出, 自旋锁的定义只有,一个结构体成员。当被包含的代码能够在很短的时间内执行完成时,那么使用自旋锁是一种很好的选择。因为自旋锁只是忙等待,不会进入睡眠。要知道睡眠是一种非常浪费时间的操作。
信号量用来在多个进程之间互斥。信号量的执行可能引起进程的睡眠,睡眠需要进程上下文的切换,这是非常浪费时间的一项工作。所以只有在一个进程对被保护的资源占用时间比进程切换的时间长很多时,信号量才是一种更好的选择,否则,会降低系统的执行效率。
2.------------------------------------------------
总共有这几个函数:
上锁,若没上到锁就一直阻塞
void down(struct semaphore *sem);
上锁,在等锁的时候,可以被中断
int __must_check down_interruptible(struct semaphore *sem);上锁,在上锁时可以被中止进程
int __must_check down_killable(struct semaphore *sem);上锁,若锁已经上了,马上返回,不等待
int __must_check down_trylock(struct semaphore *sem);上锁,若在一定的时间内没等到,就不等了
int __must_check down_timeout(struct semaphore *sem, long jiffies);解锁,信号量的值加1,如果有进程等待信号量,则唤醒这些进程
void up(struct semaphore *sem);
上面的只是接口,实际起作用的是下面的函数:
static noinline void __down(struct semaphore *sem);
static noinline int __down_interruptible(struct semaphore *sem);
static noinline int __down_killable(struct semaphore *sem);
static noinline int __down_timeout(struct semaphore *sem, long jiffies);
static noinline void __up(struct semaphore *sem);
实例代码:
struct semaphore sem;
sema_init(&sem, 1); //相当于自旋锁
if (down_interruptible (&sem))
return -ERESTARTSYS;
//xxx
up(&sem);
读写信号量
和自旋锁的读写锁类似
init_rwsem(sem)
void down_read(struct rw_semaphore *sem);
int down_read_trylock(struct rw_semaphore *sem);
void down_write(struct rw_semaphore *sem);
int down_write_trylock(struct rw_semaphore *sem);
void up_read(struct rw_semaphore *sem);
void up_write(struct rw_semaphore *sem);
3.--------------------------------------------------
函数详解:
down:
void down(struct semaphore *sem)
{
unsigned long flags;
spin_lock_irqsave(&sem->lock, flags);//上自旋锁
if (likely(sem->count > 0))//判断是否大于0
sem->count--; //减到一个使用值
else
__down(sem);
spin_unlock_irqrestore(&sem->lock, flags);//上互斥锁
}
down_interruptible:
int down_interruptible(struct semaphore *sem)
{
unsigned long flags;
int result = 0;
spin_lock_irqsave(&sem->lock, flags);
if (likely(sem->count > 0))
sem->count--;
else
result = __down_interruptible(sem);
spin_unlock_irqrestore(&sem->lock, flags);
return result;
}
down_killable:
int down_killable(struct semaphore *sem)
{
unsigned long flags;
int result = 0;
spin_lock_irqsave(&sem->lock, flags);
if (likely(sem->count > 0))
sem->count--;
else
result = __down_killable(sem);
spin_unlock_irqrestore(&sem->lock, flags);
return result;
}
down_trylock:
int down_trylock(struct semaphore *sem)
{
unsigned long flags;
int count;
spin_lock_irqsave(&sem->lock, flags);
count = sem->count - 1;
if (likely(count >= 0))
sem->count = count;
spin_unlock_irqrestore(&sem->lock, flags);
return (count < 0);
}
down_timeout
int down_timeout(struct semaphore *sem, long jiffies)
{
unsigned long flags;
int result = 0;
spin_lock_irqsave(&sem->lock, flags);
if (likely(sem->count > 0))
sem->count--;
else
result = __down_timeout(sem, jiffies);
spin_unlock_irqrestore(&sem->lock, flags);
return result;
}
up:
void up(struct semaphore *sem)
{
unsigned long flags;
spin_lock_irqsave(&sem->lock, flags);
if (likely(list_empty(&sem->wait_list)))
sem->count++;
else
__up(sem);
spin_unlock_irqrestore(&sem->lock, flags);
}