linux的信号量semaphore 的实现

这篇博客详细介绍了Linux内核中信号量Semaphore的相关结构体,包括`semaphore`和信号量等待任务描述结构体,并探讨了初始化、获取及释放信号量的函数。文章深入解析了`down`和`up`函数及其变种,如`down`函数的不同参数用法,`signal_pending_state`和`schedule_timeout`的使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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);
}

参考:https://www.cnblogs.com/hellokitty2/p/16298825.html

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值