同步 -- 信号量

本文解析了Linux内核中的信号量结构semaphore及其API,包括sema_init用于初始化、down用于获取信号量并可能进入等待、up用于释放信号量唤醒等待者。讨论了关键操作如自旋锁和任务调度的使用情况。
摘要由CSDN通过智能技术生成

 本篇文章基于Linux-6.5源码

建议:搭配Linux源码观看更佳

struct semaphore {
    raw_spinlock_t        lock; // 保护信号量的自旋锁
    unsigned int        count; // 最大同时可访问临界区的进程数量
    struct list_head    wait_list; // 等待队列,wait_list指向队列末尾
};

struct semaphore_waiter {
	struct list_head list; //表示当前任务在等待队列中的节点指针
	struct task_struct *task; //
	bool up; //任务等待状态,true或false
};

API接口

sema_init

#define __SEMAPHORE_INITIALIZER(name, n)				\
{									\
	.lock		= __RAW_SPIN_LOCK_UNLOCKED((name).lock),	\
	.count		= n,						\
	.wait_list	= LIST_HEAD_INIT((name).wait_list),		\
}

//初始化信号量sem,sem->count = val。
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);
}

down

获取信号量,获取成功,则sem->count -1。获取失败,将进程加入等待队列,开始休眠,直到已获得信号量的进程释放信号量时,会唤醒等待队列的进程。进程睡眠时,状态是TASK_UNINTERRUPTIBLE,表示睡眠过程不接受信号打断,

void __sched down(struct semaphore *sem)
{
	unsigned long flags;

	might_sleep();
    //加锁,将count操作
	raw_spin_lock_irqsave(&sem->lock, flags);
	if (likely(sem->count > 0))
		sem->count--;
	else
        //如果count已为0,则获取失败
		__down(sem); 
	raw_spin_unlock_irqrestore(&sem->lock, flags);
}
EXPORT_SYMBOL(down);

static noinline void __sched __down(struct semaphore *sem)
{
	__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;
    //把当前进程加入到信号量sem的等待队列wait_list中
	list_add_tail(&waiter.list, &sem->wait_list);
	waiter.task = current; //记录当前任务的task_struct
	waiter.up = false; //false表示在等待
    //循环
	for (;;) {
		if (signal_pending_state(state, current))
			goto interrupted;
		if (unlikely(timeout <= 0))
			goto timed_out;
		__set_current_state(state); //设置当前进程状态为不可中断状态
		raw_spin_unlock_irq(&sem->lock);
		timeout = schedule_timeout(timeout); //主动调度,等待timeout或者被wake_up
		raw_spin_lock_irq(&sem->lock);
        
        //当已获得信号量的进程释放时,会唤醒等待队列的进程,此时up为ture,跳出循环
		if (waiter.up)
			return 0;
	}

 timed_out:
	list_del(&waiter.list);
	return -ETIME;

 interrupted:
	list_del(&waiter.list);
	return -EINTR;
}

up

释放信号量:如果等待队列为空,说明没有任务在等待,则直接将count++。如果不为空,唤醒队列头部的任务,并出队。

void __sched up(struct semaphore *sem)
{
	unsigned long flags;

	raw_spin_lock_irqsave(&sem->lock, flags);
    //如果等待队列为空,说明没有任务在等待,则直接将count++
	if (likely(list_empty(&sem->wait_list)))
		sem->count++;
	else
        //否则,出队等待队列中的任务,并唤醒
		__up(sem);
	raw_spin_unlock_irqrestore(&sem->lock, flags);
}
EXPORT_SYMBOL(up);

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 = true; //表示不再等待
	wake_up_process(waiter->task); //唤醒任务
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值