如果一个任务获取信号量失败,该任务就必须等待,直到其他任务释放信号量。本文的重点是,在Linux中,当有任务释放信号量之后,如何唤醒正在等待该信号量的任务。

信号量定义如下:

struct semaphore { 	raw_spinlock_t		lock; 	unsigned int		count; 	struct list_head	wait_list; };

其中wait_list链表用于管理因没有成功获取信号量而处于睡眠状态的任务。

任务通过调用down()函数,尝试获取信号量,如果获取信号量失败,调用__down()函数。__down()函数内部调用了__down_common函数。(事实上down()函数有多个变种,如down_interruptible,在获取信号量失败时调用__down_interruptible,__down_interruptible也会调用__down_common函数。不同的down()函数最终调用__down_common时传入不同的参数,以处理不同的获取信号量的情况)。

同时,整个down()函数使用sem->lock保护起来。

void down(struct semaphore *sem) { 	unsigned long flags;  	raw_spin_lock_irqsave(&sem->lock, flags); 	if (likely(sem->count > 0)) 		sem->count--; 	else 		__down(sem); 	raw_spin_unlock_irqrestore(&sem->lock, flags); }  static noinline void __sched __down(struct semaphore *sem) { 	__down_common(sem, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT); }

下面是重点:__down_common函数如何使任务休眠,休眠中的任务如何被唤醒并获得信号量。

semaphore_waiter是一个关键的数据结构,代表一个获取信号量失败,正在等待的任务。up字段标识了该任务是否是被该信号量唤醒,也就是休眠中的任务收到某种信号被唤醒之后,判断是否是被等待中的信号量唤醒的。

struct semaphore_waiter { 	struct list_head list; 	struct task_struct *task; 	bool up; };

__down_common函数首先初始化了一个semaphore_waiter。task字段标识当前任务,up设置为false。

static inline int __sched __down_common(struct semaphore *sem, long state, 								long timeout) { 	struct semaphore_waiter waiter;  	list_add_tail(&waiter.list, &sem->wait_list); 	waiter.task = current; 	waiter.up = false; ...

然后休眠当前任务,调用 schedule_timeout()主动让出 CPU。上文提到整个函数都是在sem->lock的临界区中,但是在自旋锁的临界区是不可以休眠的,所以这里实际上在休眠之前释放了锁,被唤醒之后再重新获得锁。
当任务被唤醒后,如果waiter.up是否为真,则该任务可以获得信号量。waiter.up是必须要判断的,取决于__set_current_state()函数传入的参数不同,任务可能处于不同的休眠状态,可能被不同的信号唤醒,而未必是被等待的信号唤醒。

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

当一个任务释放信号量时,如果信号量的等待队列中存在任务,则将队列中的第一个任务的up标记为true,并唤醒,同时从等待队列中删除。
同时,只有在等待队列为空的情况下,才会更新sem->count,确保了等待队列中的任务优先于新来的任务获得信号量,保证了严格的先进先出,不会因为新来的任务导致等待队列中的任务饥饿。

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 = true; 	wake_up_process(waiter->task); }

任务被唤醒之后,检测到up为true,返回0,成功获得信号量。

热门文章

为什么猫不打疫苗不能绝育呢视频(家猫为什么不打疫苗)

摘要: 本篇文章给大家谈谈为什么猫不打疫苗不能绝育呢视频,以及家猫为什么不打疫苗对应的知识点,希望对各位有所帮助,不要忘了收藏本站喔。本文目录一览:1、猫没打疫苗可以绝育吗...

2月28日更新19.9M/S,2025年最新高速V2ray/Clash/SSR/Shadowrocket订阅链接免费节点地址分享

这一次的节点更新覆盖了加拿大、美国、欧洲、香港、新加坡、韩国、日本等地区,最高速度可达19.9 M/S。只需复制下方的Clash/v2ray订阅链接,在客户端添加后即可正常使用。

2月4日更新18.9M/S,2025年最新高速Clash/Shadowrocket/SSR/V2ray订阅链接免费节点地址分享

这一次的节点更新覆盖了加拿大、新加坡、韩国、美国、香港、欧洲、日本等地区,最高速度可达18.9 M/S。只需复制下方的Clash/v2ray订阅链接,在客户端添加后即可正常使用。

动物诊疗许可证申领准备材料清单(动物诊疗许可证申领条件)

摘要: 本篇文章给大家谈谈动物诊疗许可证申领准备材料清单,以及动物诊疗许可证申领条件对应的知识点,希望对各位有所帮助,不要忘了收藏本站喔。本文目录一览:1、开宠物店需要办理哪些手续?具..

人和宠物打的狂犬疫苗一样吗 人和宠物打的狂犬疫苗一样吗多少钱

摘要: 大家好,今天小编关注到一个比较有意思的话题,就是关于人和宠物打的狂犬疫苗一样吗的问题,于是小编就整理了4个相关介绍人和宠物打的狂犬疫苗一样吗的解答,让我们一起看看吧。狗打了狂犬疫苗

1月14日更新19.8M/S,2025年最新高速Clash/V2ray/Shadowrocket/SSR订阅链接免费节点地址分享

这一次的节点更新覆盖了美国、加拿大、香港、韩国、欧洲、新加坡、日本等地区,最高速度可达19.8 M/S。只需复制下方的Clash/v2ray订阅链接,在客户端添加后即可正常使用。

重庆猫领养(重庆猫网领养)

摘要: 今天给各位分享重庆猫领养的知识,其中也会对重庆猫网领养进行解释,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧!本文目录一览:1、重庆江津区猫领养中心地址...

2月26日更新20.9M/S,2025年最新高速SSR/Shadowrocket/V2ray/Clash订阅链接免费节点地址分享

这一次的节点更新覆盖了韩国、新加坡、加拿大、美国、欧洲、日本、香港等地区,最高速度可达20.9 M/S。只需复制下方的Clash/v2ray订阅链接,在客户端添加后即可正常使用。

厦门市宠物领养中心地址(厦门领养宠物猫中心)

摘要: 今天给各位分享厦门市宠物领养中心地址的知识,其中也会对厦门领养宠物猫中心进行解释,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧!本文目录一览:1、厦门宠物猫在哪买..

3月3日更新20.5M/S,2025年最新高速Clash/Shadowrocket/SSR/V2ray订阅链接免费节点地址分享

这一次的节点更新覆盖了香港、日本、韩国、加拿大、美国、欧洲、新加坡等地区,最高速度可达20.5 M/S。只需复制下方的Clash/v2ray订阅链接,在客户端添加后即可正常使用。

归纳