这篇文章来自我的个人博客里 www.mystone7.com 现在已经关掉了
Linux 中的信号量是一种资源锁,如果有一个任务试图获得一个已经被占用的信号量时,信号量会将其推到一个等待队列中,这时处理器会重获自由从而去执行其它代码,当持有信号量的进程将信号量释放后,处于等待队列中的那个任务将会被唤醒,并将获得该信号量。
信号量分为互斥信号量,以及计数信号量,区别在于 count的值,若为1,则为互斥信号量,若大于1 则为计数信号量。
信号量结构体
struct semaphore {
spinlock_t lock; //防止其它进程同时访问该信号量,并禁止中断
unsigned int count; //计值信号量
struct list_head wait_list; //加入等待队列的指针
};
信号量初始化
#define __SEMAPHORE_INITIALIZER(name,n) \
{ \
.lock =__SPIN_LOCK_UNLOCKED((name).lock), \
.count = n, \
.wait_list =LIST_HEAD_INIT((name).wait_list), \
}
定义一个信号量并初始化
#define DEFINE_SEMAPHORE(name) \
struct semaphore name = __SEMAPHORE_INITIALIZER(name, 1)
初始化一个计值信号量
static inline void sema_init(structsemaphore *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);
}
获取一个信号量
extern void down(structsemaphore *sem); //内核说已经过时了
extern int __must_check down_interruptible(struct semaphore *sem); //acquirethe semaphore unless interrupted, If the sleep is interrupted by a signal, thisfunction will return -EINTR.
extern int __must_check down_killable(struct semaphore *sem); //acquirethe semaphore unless killed, If the sleep is interrupted by a fatal signal,this function will return -EINTR
extern int __must_check down_trylock(struct semaphore *sem); //try toacquire the semaphore, without waiting
extern int __must_check down_timeout(struct semaphore *sem, long jiffies);//acquire the semaphore within a specified time, If the semaphore is notreleased within the specified number of jiffies, this function returns -ETIME.
extern void up(structsemaphore *sem);
以down_interruptible为例
/**
*down_interruptible - acquire the semaphore unless interrupted
*@sem: the semaphore to be acquired
*
*Attempts to acquire the semaphore. If nomore tasks are allowed to
*acquire the semaphore, calling this function will put the task to sleep.
* If the sleep is interrupted by a signal, thisfunction will return -EINTR.
* Ifthe semaphore is successfully acquired, this function returns 0.
*/
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--; //计数值减1
else
result = __down_interruptible(sem); //在切换前会开中断,
spin_unlock_irqrestore(&sem->lock, flags); //切换后开中断
return result;
}
EXPORT_SYMBOL(down_interruptible);
static noinline int __sched __down_interruptible(struct semaphore *sem)
{
return __down_common(sem, TASK_INTERRUPTIBLE,MAX_SCHEDULE_TIMEOUT);
}
TASK_INTERRUPTIBLE 可被信号中断唤醒
#define MAX_SCHEDULE_TIMEOUT LONG_MAX 类似于无限睡下去
/*
*Because this function is inlined, the 'state' parameter will be
*constant, and thus optimised away by the compiler. Likewise the
*'timeout' parameter for the cases without timeouts.
*/
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)) //如果该进程被信号中断,则退出,task是在哪被设置了这个任号的呢?遗留问题
goto interrupted;
if (timeout <= 0) //定时到了
goto timed_out;
__set_task_state(task, state); //将task的TASK_RUNNING状态变成TASK_INTERRUPTIBEU状态,当该进程被切换出去时,如果没有被置为TASK_RUNNING状态,调度器则不会在调度它了
spin_unlock_irq(&sem->lock); //吻合前面的spin_lock_irqsave,开中断
timeout =schedule_timeout(timeout); //将进程切换出去
spin_lock_irq(&sem->lock);//关中断,吻合后面的spin_unlock_irqsave
if (waiter.up)
return 0;
}
timed_out:
list_del(&waiter.list);
return -ETIME;
interrupted:
list_del(&waiter.list);
return -EINTR;
}
struct semaphore_waiter {
struct list_head list;
struct task_struct *task; //等待信号量的任务
int up;
};
timeout= schedule_timeout(timeout); //将进程切换出去
当定时timeout时间到过后,返回值timeout变为0, 则__down_common返回-ETIME.
现在假设有4个进程A B C D, A已经获得一个信号量global,而B C D正在按顺序等待该信号量
Lock
Count
wait_list
list
up=0
* task=B
list
up=0
* task=d
list
up=0
* task=C
唤醒函数,释放信号量
/**
* up- release the semaphore
*@sem: the semaphore to release
*
*Release the semaphore. Unlike mutexes,up() may be called from any
*context and even by tasks which have never called down().
*/
void up(struct semaphore *sem)
{
unsigned long flags;
spin_lock_irqsave(&sem->lock, flags);
if (likely(list_empty(&sem->wait_list))) //
sem->count++; //如果队列为空的话,则将计数值加1
else
__up(sem); //唤醒
spin_unlock_irqrestore(&sem->lock, flags);
}
EXPORT_SYMBOL(up);
static noinline void __sched __up(structsemaphore *sem)
{
struct semaphore_waiter *waiter =list_first_entry(&sem->wait_list, struct semaphore_waiter, list);
list_del(&waiter->list); //根据队列,先进先出的原则,第一个down的进程会被先唤醒,将该进程从信号量的等待队列中删除
waiter->up = 1; //与down里的if(waiter.up)对应
wake_up_process(waiter->task); //唤醒进程,将其加入到可运行的进程链表中进程state的状态由TASK_WAKING逐渐变成TASK_RUNNING中去
}
/**
*wake_up_process - Wake up a specific process
*@p: The process to be woken up.
*
*Attempt to wake up the nominated process and move it to the set of runnable
*processes. Returns 1 if the process waswoken up, 0 if it was already
*running.
*
* Itmay be assumed that this function implies a write memory barrier before
*changing the task state if and only if any tasks are woken up.
*/
int wake_up_process(struct task_struct *p)
{
return try_to_wake_up(p, TASK_ALL, 0);
}
EXPORT_SYMBOL(wake_up_process);