以下内容转自: 信号量
一,信号量和自旋锁的区别
信号量与自旋锁有一定的区别,信号量在无法得到资源时,内核线程处于睡眠阻塞状态,而自旋锁处于忙等待状态。因此,如果资源被占用时间很短时,使用自旋锁较好,因为它可节约调度时间。如果资源被占用的时间较长,使用信号量较好。
二,信号量相关的API
信号量的相关API
函数定义 | 功能说明 |
sema_init(struct semaphore *sem, int val) | 初始化信号量,将信号量计数器值设置val。 |
down(struct semaphore *sem) | 获取信号量,不建议使用此函数。 |
down_interruptible(struct semaphore *sem) | 可被中断地获取信号量,如果睡眠被信号中断,返回错误-EINTR。 |
down_killable (struct semaphore *sem) | 可被杀死地获取信号量。如果睡眠被致命信号中断,返回错误-EINTR。 |
down_trylock(struct semaphore *sem) | 尝试原子地获取信号量,如果成功获取,返回0,不能获取,返回1。 |
down_timeout(struct semaphore *sem, long jiffies) | 在指定的时间jiffies内获取信号量,若超时未获取,返回错误-ETIME。 |
up(struct semaphore *sem) | 释放信号量sem。 |
读写信号量的相关API
API函数定义 | 功能说明 |
DECLARE_RWSEM(name) | 声明名为name的读写信号量,并初始化它。 |
void init_rwsem(struct rw_semaphore *sem); | 对读写信号量sem进行初始化。 |
void down_read(struct rw_semaphore *sem); | 读者用来获取sem,若没获得时,则调用者睡眠等待。 |
void up_read(struct rw_semaphore *sem); | 读者释放sem。 int down_read_trylock(struct rw_semaphore *sem); 读者尝试获取sem,如果获得返回1,如果没有获得返回0。可在中断上下文使用。 |
void down_write(struct rw_semaphore *sem); | 写者用来获取sem,若没获得时,则调用者睡眠等待。 |
int down_write_trylock(struct rw_semaphore *sem); | 写者尝试获取sem,如果获得返回1,如果没有获得返回0。可在中断上下文使用 |
void up_write(struct rw_semaphore *sem); | 写者释放sem。 |
void downgrade_write(struct rw_semaphore *sem); | 把写者降级为读者。 |
三,信号量相关的函数
信号量的结构体:
struct semaphore {
spinlock_t lock;
unsigned int count;
struct list_head wait_list;
};
读写信号量的结构, 在include/linux/rwsem-spinlock.h中
struct rw_semaphore {
/*读/写信号量定义:
* - 如果activity为0,那么没有激活的读者或写者。
* - 如果activity为+ve,那么将有ve个激活的读者。
* - 如果activity为-1,那么将有1个激活的写者。 */
__s32 activity; /*信号量值*/
spinlock_t wait_lock; /*用于锁等待队列wait_list*/
struct list_head wait_list; /*如果非空,表示有进程等待该信号量*/
#ifdef CONFIG_DEBUG_LOCK_ALLOC /*用于锁调试*/
struct lockdep_map dep_map;
#endif
};
读者加锁
读者加锁是通过down_read()函数完成的,
void __sched __down_read(struct rw_semaphore *sem)
{
struct rwsem_waiter waiter;
struct task_struct *tsk;
spin_lock_irq(&sem->wait_lock);
/*如果有0或多个读者,并且等待队列为空,就可以获取sem*/
if (sem->activity >= 0 && list_empty(&sem->wait_list)) {
/* 获得sem */
sem->activity++; /*读者计数加1*/
spin_unlock_irq(&sem->wait_lock);
goto out;
}
/*运行到这里,说明不能获取sem,将当前进程加入等待队列进行等待*/
tsk = current;
set_task_state(tsk, TASK_UNINTERRUPTIBLE);
/* 建立等待队列成员*/
waiter.task = tsk;
waiter.flags = RWSEM_WAITING_FOR_READ; /*表示等待读操作*/
get_task_struct(tsk); /*进程使用计数加1*/
list_add_tail(&waiter.list, &sem->wait_list); /*将等待成员加到等待队列尾*/
/* 不再需要访问等待队列,因此,这里解锁*/
spin_unlock_irq(&sem->wait_lock);
/* 读者等待获取sem */
for (;;) {
if (!waiter.task)
break;
schedule();
set_task_state(tsk, TASK_UNINTERRUPTIBLE);
}
/*运行这里,退出等待,说明可以获取sem了*/
tsk->state = TASK_RUNNING;
out:
;
}
读者解锁
void __up_read(struct rw_semaphore *sem)
{
unsigned long flags;
spin_lock_irqsave(&sem->wait_lock, flags);
/*如果所有读者完成读操作,并且有写者等待,那么唤醒一个写者*/
if (--sem->activity == 0 && !list_empty(&sem->wait_list))
sem = __rwsem_wake_one_writer(sem);
spin_unlock_irqrestore(&sem->wait_lock, flags);
}
/*唤醒一个写者*/
static inline struct rw_semaphore *__rwsem_wake_one_writer(struct rw_semaphore *sem)
{
struct rwsem_waiter *waiter;
struct task_struct *tsk;
sem->activity = -1; /*表示有一个写者正在写操作*/
/*获取一个等待者*/
waiter = list_entry(sem->wait_list.next, struct rwsem_waiter, list);
list_del(&waiter->list); /*将该等待者从等待队列删除*/
tsk = waiter->task;
smp_mb(); /*加内存屏障,确保完成上面的指针引用操作*/
waiter->task = NULL;
wake_up_process(tsk); /*唤醒进程*/
put_task_struct(tsk); /*进程上下文使用计数减1*/
return sem;
}
写者加锁
void __sched down_write(struct rw_semaphore *sem)
{
might_sleep();
rwsem_acquire(&sem->dep_map, 0, 0, _RET_IP_);
LOCK_CONTENDED(sem, __down_write_trylock, __down_write);
}
void __sched __down_write(struct rw_semaphore *sem)
{
__down_write_nested(sem, 0);
}
函数__down_write_nested完成加写者锁的具体操作。当没有读者或写者操作时,写者才可以获取写者锁。写者锁是独占的。如果有其他写者或读者操作时,写者必须等待。其列出如下:
void __sched __down_write_nested(struct rw_semaphore *sem, int subclass)
{
struct rwsem_waiter waiter;
struct task_struct *tsk;
spin_lock_irq(&sem->wait_lock);
/*如果没有读者,并且等待队列为空(说明没有写者)时,写者才能获取写者锁*/
if (sem->activity == 0 && list_empty(&sem->wait_list)) {
/* 获取写者锁*/
sem->activity = -1;
spin_unlock_irq(&sem->wait_lock);
goto out;
}
/*运行到这里,说明有读者或写者在操作,需要等待*/
tsk = current;
set_task_state(tsk, TASK_UNINTERRUPTIBLE);
/* 建立等待队列成员*/
waiter.task = tsk;
waiter.flags = RWSEM_WAITING_FOR_WRITE; /*标识为等待写操作*/
get_task_struct(tsk); /*进程上下文使用计数加1*/
list_add_tail(&waiter.list, &sem->wait_list); /*加到等待队列尾*/
spin_unlock_irq(&sem->wait_lock);
/* 进行等待*/
for (;;) {
if (!waiter.task)
break;
schedule();
set_task_state(tsk, TASK_UNINTERRUPTIBLE);
}
/*被唤醒*/
tsk->state = TASK_RUNNING;
out:
;
}
解写者锁
函数up_write释放写者锁,将读者计数设置为0,其列出如下:
void up_write(struct rw_semaphore *sem)
{
rwsem_release(&sem->dep_map, 1, _RET_IP_);
__up_write(sem);
}
void __up_write(struct rw_semaphore *sem)
{
unsigned long flags;
spin_lock_irqsave(&sem->wait_lock, flags);
sem->activity = 0; /*表示有0个读者*/
if (!list_empty(&sem->wait_list))
sem = __rwsem_do_wake(sem, 1); /*唤醒等待者*/
spin_unlock_irqrestore(&sem->wait_lock, flags);
}