1.semaphore 的相关结构体
1.1 semaphore
位于include\linux\semaphore.h
struct semaphore {
raw_spinlock_t lock;//实现需要spinlock
/*
* 对于二值信号量,为1表示没有任务在临界区,为0表示只有1个任
* 务在临界区,没有任务等待 在改信号量上,为-n表示有n个任务等
* 待在该信号量上
*/
unsigned int count;//允许使用的人数量
/*
* 获取不到信号量的任务对应的semaphore_waiter结构会串联在这个
* 链表上,先入先出。
*/
struct list_head wait_list;//等待信号量的线程放到这个链表
};
1.2 信号量等待任务描述结构体
位于kernel\locking\semaphore.c
struct semaphore_waiter {
/* 通过此成员挂在semaphore结构的wait_list链表上 */
struct list_head list;
/* 等待信号量的任务 */
struct task_struct *task;
/*
* 被释放信号量的行为唤醒的情况下为真,其它比如
* 信号、超时而导致的唤醒为假。
*/
bool up;
};
2.semaphore 的相关函数
2.1 初始化
//定义并初始化一个二值信号量
DEFINE_SEMAPHORE(sem1);
//初始化一个二值或非二值的信号量
void sema_init(struct semaphore *sem, int val);
2.2 获取信号量函数
//获取信号量,若是获取不到进入 TASK_UNINTERRUPTIBLE 状态
void down(struct semaphore *sem);
//同 down(),区别是若获取不到进入 TASK_INTERRUPTIBLE 状态,支持被信号唤醒
int __must_check down_interruptible(struct semaphore *sem);
//同 down(),区别是若获取不到进入 TASK_KILLABLE(TASK_WAKEKILL | TASK_UNINTERRUPTIBLE)状态,表示只可被 SIGKILL 信号唤醒
int __must_check down_killable(struct semaphore *sem);
//尝试获取信号量,不会阻塞,返回1成功,0失败
int __must_check down_trylock(struct semaphore *sem);
//同 down(),区别是若获取不到不会一直等待,而是有一个超时时间,到期后自动唤醒
int __must_check down_timeout(struct semaphore *sem, long jiffies);
2.3 释放信号量函数
void up(struct semaphore *sem);
3.函数详解
3.1 down函数
/*
* down - 获取信号量
*
* 获取信号量函数,若此信号量不允许更多任务获取它时,此函数会让任务进入休眠状态
* 直到有人释放信号量。
*
* 此函数已经被弃用了,请使用 down_interruptible() 或 down_killable() 替代。
*/
void down(struct semaphore *sem) //kernel/locking/semaphore.c
{
unsigned long flags;
raw_spin_lock_irqsave(&sem->lock, flags);
/* 或信号量值大于0,直接将去1然后返回,信号量值小于等于0时才会阻塞 */
if (likely(sem->count > 0))
sem->count--;
else
__down(sem);
raw_spin_unlock_irqrestore(&sem->lock, flags);
}
EXPORT_SYMBOL(down);
static noinline void __sched __down(struct semaphore *sem)
{
/*
* down() 获取不到信号量会进入D状态,而且无限超时。
*
* down() 的其它衍生函数都会调用到 __down_common() 只是传参不同。
*/
__down_common(sem, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
}
static inline int __sched __down_common(struct semaphore *sem, long state,
long timeout)
{
struct semaphore_waiter waiter;
/*
* 将当前任务放在链表末尾部,up初始化为假(唤醒后,被释放信号量的行
* 为唤醒的情况下为真,被信号、等待超时而导致的唤醒为假)
*/
list_add_tail(&waiter.list, &sem->wait_list);
waiter.task = current;
waiter.up = false;
for (;;) {
/*
* down()的三个衍生函数响应不同:
* down() 不起作用,D状态不支持信号唤醒。
* down_interruptible() 这里可能被信号唤醒。
* down_killable() 这里只可能被SIGKILL信号唤醒。
*/
if (signal_pending_state(state, current))
goto interrupted;
if (unlikely(timeout <= 0))/* 定时时间到期,只有 down_timeout() 可能从这里退出 */
goto timed_out;
__set_current_state(state);/* 将当前进程的状态设置为非RUNNING状态,准备休眠 */
raw_spin_unlock_irq(&sem->lock);
timeout = schedule_timeout(timeout); /* 触发切换,当前进程让出CPU */
raw_spin_lock_irq(&sem->lock);
if (waiter.up)/* 若是被释放信号量的行为唤醒的,返回0,见 up() 的讲解 */
return 0;
}
timed_out:
list_del(&waiter.list);/* 对于 down_timeout() 等待超时后,将当前任务从链表上删除,返回-ETIME */
return -ETIME;
interrupted:
list_del(&waiter.list);/* 对于down_interruptible()和down_killable()被信号唤醒返回-EINTR */
return -EINTR;
}
3.2 down函数的几个变种函数
这几个函数指示传参不相同而已。
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 timeout)
{
return __down_common(sem, TASK_UNINTERRUPTIBLE, timeout);
}
3.3 signal_pending_state
static inline int signal_pending_state(long state, struct task_struct *p)
{
/* 若state传参 TASK_INTERRUPTIBLE, 这里直接就退出了 */
if (!(state & (TASK_INTERRUPTIBLE | TASK_WAKEKILL)))
return 0;
if (!signal_pending(p))
return 0;
/*对于 TASK_KILLABLE(TASK_WAKEKILL | TASK_UNINTERRUPTIBLE),只能被SIGKILL唤醒*/
return (state & TASK_INTERRUPTIBLE) || __fatal_signal_pending(p);
}
3.4 schedule_timeout
/*
* schedule_timeout - 睡眠直到超时
* @timeout: 超时值,单位 jiffies
*
* 使当前任务休眠,直到 @timeout jiffies 过去。函数行为取决于当前任务状态(另
* 见 set_current_state() 描述):
*
* %TASK_RUNNING - 调度程序被调用,但任务根本不休眠。发生这种情况是因为 sched_submit_work()
* 对处于 %TASK_RUNNING 状态的任务不执行任何操作。
*
* %TASK_UNINTERRUPTIBLE - 保证至少在 @timeout jiffies 之后才返回,除非当前任务被显式唤醒
* (例如通过wake_up_process())。
*
* %TASK_INTERRUPTIBLE - 如果信号被传递到当前任务或当前任务被显式唤醒,可能会提前返回。
*
* 当此函数返回时,当前任务状态保证为 %TASK_RUNNING。
*
* @timeout 若被指定为 %MAX_SCHEDULE_TIMEOUT,将直接切走而不受超时限制。在这种情况下,
* 返回值将是 %MAX_SCHEDULE_TIMEOUT。
*
* 当定时器超时返回 0,否则将返回剩余的 jiffies 时间。在所有情况下,返回值都保证为非负数。
*/
signed long __sched schedule_timeout(signed long timeout) //kernel/time/timer.c
{
struct process_timer timer;
unsigned long expire;
switch (timeout)
{
case MAX_SCHEDULE_TIMEOUT:
/* 若指定的是无限超时,直接切走,不会触发定时器定时 */
schedule();
goto out;
default:
/* 这个情况永远也不应该发生,打印一个报错提示 */
if (timeout < 0) {
printk(KERN_ERR "schedule_timeout: wrong timeout value %lx\n", timeout);
dump_stack();
current->state = TASK_RUNNING;
goto out;
}
}
/* 定时器到期时间指定为当前jiffies值加上指定的到期时间 */
expire = timeout + jiffies;
/* 指定为当前将被阻塞的任务 */
timer.task = current;
timer_setup_on_stack(&timer.timer, process_timeout, 0);
/* 启动定时器 */
__mod_timer(&timer.timer, expire, MOD_TIMER_NOTPENDING);
/* 将当前任务切走 */
schedule();
/* 当前任务被唤醒后继续执行 */
del_singleshot_timer_sync(&timer.timer);
/* Remove the timer from the object tracker */
destroy_timer_on_stack(&timer.timer);
/* 检查超时时间是否到期,到期返回0否则返回剩余的jiffies时间 */
timeout = expire - jiffies;
out:
return timeout < 0 ? 0 : timeout;
}
EXPORT_SYMBOL(schedule_timeout);
/* 超时唤醒函数 */
static void process_timeout(struct timer_list *t) //kernel/time/timer.c
{
struct process_timer *timeout = from_timer(timeout, t, timer);
wake_up_process(timeout->task);
}
3.2 up函数
static noinline void __sched __up(struct semaphore *sem) //kernel/locking/semaphore.c
{
/* 从链表首取待唤醒的任务进行唤醒 */
struct semaphore_waiter *waiter = list_first_entry(&sem->wait_list, struct semaphore_waiter, list);
/* 先从wait链表上取下来 */
list_del(&waiter->list);
/* 被up唤醒的任务up成员为true */
waiter->up = true;
/* 执行唤醒选核流程 */
wake_up_process(waiter->task);
}