一、内核相关文件为include/linux/semaphore.h和kernel/semaphore.c
二、主要结构体:
-
struct
semaphore {
-
raw_spinlock_t lock;
-
unsigned
int count;
-
struct
list_head wait_list;
-
};
结构体成员变量解读:
1、lock主要用于保护count和wait_list链表的访问;
2、count记录信号量等待进程的计数;
3、wait_list用于记录等待信号量的进程,串成一个链表来统一管理;
三、相关接口
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);
-
}
接口主要完成互斥锁、信号量计数初始值、链表头结点的初始化操作;
2、down信号量接口
获取信号量接口总共有五个:
-
extern void down(struct semaphore *sem);
-
extern int __must_check down_interruptible(struct semaphore *sem);
-
extern int __must_check down_killable(struct semaphore *sem);
-
extern int __must_check down_trylock(struct semaphore *sem);
-
extern int __must_check down_timeout(struct semaphore *sem, long jiffies);
1)down接口;uninterruptible
2)down信号量接口,一般都建议使用down_interruptible,此接口将会把当前任务状态设置为TASK_INTERRUPTIBLE,意味着一旦满足条件,任务将会在设定的超时时间到来之前被调度;
3)down_killable:可被杀死地获取信号量。如果睡眠被致命信号中断,返回错误-EINTR;
4)down_trylock:此接口上来就去获取信号量,不会设置超时时间,不做任何等待,获取到就返回0,获取不到就返回1;
5)down_timeout:如果上来就获取到信号量,则直接返回,获取不到则睡眠等待设置的超时时间时长;
这五个接口,除了down_trylock,最终都调用了__down_common这个接口,只不过传入的参数不同而已
-
static noinline
void __sched __down(
struct semaphore *sem)
-
{
-
__down_common(sem, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
-
}
-
-
static noinline
int __sched __down_interruptible(
struct semaphore *sem)
-
{
-
return __down_common(sem, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
-
}
-
-
static noinline
int __sched __down_killable(
struct semaphore *sem)
-
{
-
return __down_common(sem, TASK_KILLABLE, MAX_SCHEDULE_TIMEOUT);
-
}
-
-
static noinline
int __sched __down_timeout(
struct semaphore *sem,
long jiffies)
-
{
-
return __down_common(sem, TASK_UNINTERRUPTIBLE, jiffies);
-
}
我们可以看一下__down_common这个接口:首先会把当前进程挂接到信号量的Wait_list链表中,然后会通过schedule_timeout调度其他进程,当前任务挂起;
-
struct
semaphore_waiter {
-
struct
list_head list;
-
struct
task_struct *task;
-
int up;
-
};
-
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);
-
if (waiter.up)
-
return
0;
-
}
-
-
timed_out:
-
list_del(&waiter.list);
-
return -ETIME;
-
-
interrupted:
-
list_del(&waiter.list);
-
return -EINTR;
-
}
3、释放信号量接口:此接口主要做了以下工作,首先判断当前信号量的wait_list是否为空,如果为空,即没有任何进程等待此信号量,则直接sem->count++,否则,则把wait_list中第一个节点摘除,通过wake_up_process放置到激活任务队列中,等待执行。
-
extern void up(struct semaphore *sem);
-
void up(struct semaphore *sem)
-
{
-
unsigned
long flags;
-
-
raw_spin_lock_irqsave(&sem->lock, flags);
-
if (
likely(
list_empty(&sem->wait_list)))
-
sem->count++;
-
else
-
__up(sem);
-
raw_spin_unlock_irqrestore(&sem->lock, flags);
-
}
-
static noinline
void __sched __up(
struct semaphore *sem)
-
{
-
struct
semaphore_waiter *waiter =
list_first_entry(&sem->wait_list,
-
struct semaphore_waiter, list);
-
list_del(&waiter->list);
-
waiter->up =
1;
-
wake_up_process(waiter->task);
-
}