信号量实现

 自旋锁是一种忙等的锁,会一直占用cpu;而信号量则运行进程进入睡眠状态,即获取锁失败的进程可以进行睡眠,主动让出cpu的使用权。

信号量定义:

struct semaphore {
    raw_spinlock_t        lock;//用于保护成员count和wait_list
    unsigned int        count;//允许进入临界区的内核执行路径个数
    struct list_head    wait_list;//用于管理在该信号量上睡眠的进程
};

自旋锁初始化:

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

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


#define __RAW_SPIN_LOCK_INITIALIZER(lockname)	\
	{					\
	.raw_lock = __ARCH_SPIN_LOCK_UNLOCKED,	\
	SPIN_DEBUG_INIT(lockname)		\
	SPIN_DEP_MAP_INIT(lockname) }

#define __RAW_SPIN_LOCK_UNLOCKED(lockname)	\
	(raw_spinlock_t) __RAW_SPIN_LOCK_INITIALIZER(lockname)

down获取信号量,up释放信号量

down:访问count需要独占访问。使用raw_spin_lock_irqsave保证独占访问,屏蔽本cpu中断。如果count>0表示还有资源(count)能够访问,成功获取信号量。将count--。反之获取信号量失败,进入__down()。

可以看到count可以被看做资源的个数或者说是表示能够同时指出多少个人访问。即信号量支持同时被多人访问。当count被初始化为1时,它就变得和自旋锁类似,变为了独占访问,但是信号量可以睡眠,自旋锁不行。

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

 __down_common:一个死循环,在这个循环中不断的调用schedule_timeout让出cpu,指定信号量释放后被唤醒,或者被其他信号打断,才退出循环。

1、有待处理的信号;2、等待信号量超时;3、其他人释放信号量被唤醒(即成功获得信号量)

那如果是前两个原因导致的返回,这不代表信号量并未获取成功吗?但是__down并未对返回值进行判断,他们是怎么知道是成功获取了信号量,还是并未获取到呢???明天在看看

解答:

1、可以看到__down_common里面传入的TASK_UNINTERRUPTIBLE:一种不可中断的睡眠状态,不可以被信号打断,必须等到等待的条件满足时才被唤醒。TASK_UNINTERRUPTIBLE只能被wake_up()唤醒

2、signal_pending_state函数实现,可以看到state为TASK_UNINTERRUPTIBLE一直都是返回的0,也从代码上验证了不可以被信号打断

3、__down_common传入的时间是MAX_SCHEDULE_TIMEOUT,在函数schedule_timeout的实现可以看到必然不会因为超时返回。

基于以上三点原因,得出down函数的返回只可能是获取到信号量而返回,因此也不需要对返回值进行判断。而对于其他函数,eg down_interruptible等,则需要返回值进行判断,详情见文末。

static inline int signal_pending_state(long state, struct task_struct *p)
{
	if (!(state & (TASK_INTERRUPTIBLE | TASK_WAKEKILL)))
		return 0;
	if (!signal_pending(p))
		return 0;

	return (state & TASK_INTERRUPTIBLE) || __fatal_signal_pending(p);
}
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 task_struct *task = current;
	struct semaphore_waiter waiter;

	/* 下面三条语句就是将当前进程加入到了信号量的等待队列中 */
	list_add_tail(&waiter.list, &sem->wait_list);
	waiter.task = task;
	waiter.up = false;

	for (;;) {
		/* 有待处理的信号,则需要退出去 */
		if (signal_pending_state(state, task))
			goto interrupted;
		/* 等待信号量超时也退出 */
		if (unlikely(timeout <= 0))
			goto timed_out;
		__set_task_state(task, state);
		raw_spin_unlock_irq(&sem->lock);
		/* 让出cpu一段时间 */
		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;
}

 对于down:schedule_timeout传入的MAX_SCHEDULE_TIMEOUT。因此对于down的动作就是不断调用schedule主动换下cpu,当被再次调度的时候,继续检查被唤醒的原因。如果进程是超时或者被其他人发生信号,则转到timeout或者interrupted处。如果是waiter.up == true,则表示其他人释放了信号量被唤醒。

signed long __sched schedule_timeout(signed long timeout)
{
	struct timer_list 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;
		}
	}

	expire = timeout + jiffies;

	setup_timer_on_stack(&timer, process_timeout, (unsigned long)current);
	__mod_timer(&timer, expire, false, TIMER_NOT_PINNED);
	schedule();
	del_singleshot_timer_sync(&timer);

	/* Remove the timer from the object tracker */
	destroy_timer_on_stack(&timer);

	timeout = expire - jiffies;

 out:
	return timeout < 0 ? 0 : timeout;
}

信号量释放

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

 可以看到信号量的等待队列是先进先出的规则进行。在获取信号量失败时,将失败的进程加入到链表最后面;这里唤醒进程的时候,则是从头部获取节点。

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

down_interruptible:可以看到该函数就需要对返回值进行判断,以便知道是否成功获取信号量

int down_interruptible(struct semaphore *sem)
{
	unsigned long flags;
	int result = 0;

	raw_spin_lock_irqsave(&sem->lock, flags);
	if (likely(sem->count > 0))
		sem->count--;
	else
		result = __down_interruptible(sem);
	raw_spin_unlock_irqrestore(&sem->lock, flags);

	return result;
}

 同样看传入的参数TASK_INTERRUPTIBLE和MAX_SCHEDULE_TIMEOUT。其中TASK_INTERRUPTIBLE是可以被信号和wake_up()唤醒,当信号到来时,进程会被设置为可运行

因此对于这个函数存在成功获取信号量和因为待处理的信号导致信号量获取失败的两种情况。所以需要返回值辅助判断。 

static noinline int __sched __down_interruptible(struct semaphore *sem)
{
	return __down_common(sem, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
}

相似的还有__down_timeout以及__down_killable等

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在RT-Thread中,信号量是一种用于线程同步的机制,它用于保护共享资源,避免多个线程同时访问同一资源而导致的冲突。信号量可以分为二值信号量和计数信号量两种类型。 一、二值信号量 二值信号量是一种只有0和1两种状态的信号量,它常用于互斥的情况,即只能有一个线程访问共享资源。在RT-Thread中,二值信号量实现比较简单,可以使用一个全局变量来表示其状态,通过设置和清除变量的值来实现信号量的获取和释放。 二、计数信号量 计数信号量是一种用于控制可重入资源的信号量,它的值可以大于1,表示可以有多个线程同时访问共享资源。在RT-Thread中,计数信号量实现比较复杂,需要使用一个等待队列来管理等待该信号量的线程,当信号量的值大于0时,可以直接获取信号量,否则需要将当前线程加入等待队列,等待其他线程释放信号量后再次尝试获取。 下面是一个简单的计数信号量实现示例: ``` typedef struct semaphore { rt_uint16_t value; /* 信号量的值 */ rt_list_t suspend_thread; /* 等待该信号量的线程队列 */ }semaphore_t; void rt_sem_init(semaphore_t *sem, rt_uint16_t value) { sem->value = value; rt_list_init(&(sem->suspend_thread)); } void rt_sem_take(semaphore_t *sem) { rt_base_t level; level = rt_hw_interrupt_disable(); if (sem->value > 0) /* 信号量的值大于0,可以直接获取 */ { sem->value--; rt_hw_interrupt_enable(level); return; } else /* 信号量的值为0,需要将当前线程加入等待队列 */ { rt_current_thread->error = RT_EOK; rt_list_insert_before(&(sem->suspend_thread), &(rt_current_thread->tlist)); rt_hw_interrupt_enable(level); rt_thread_suspend(rt_current_thread); } } void rt_sem_release(semaphore_t *sem) { rt_base_t level; struct rt_thread *thread; level = rt_hw_interrupt_disable(); if (rt_list_isempty(&(sem->suspend_thread))) /* 没有等待该信号量的线程 */ { sem->value++; } else /* 有等待该信号量的线程,需要唤醒其中一个线程 */ { thread = rt_list_entry(sem->suspend_thread.next, struct rt_thread, tlist); rt_list_remove(&(thread->tlist)); rt_hw_interrupt_enable(level); rt_thread_resume(thread); } rt_hw_interrupt_enable(level); } ``` 以上是一个简单的计数信号量实现示例,其中rt_list_t是RT-Thread中的链表结构,用于管理等待队列;rt_hw_interrupt_disable和rt_hw_interrupt_enable分别用于关闭和开启中断,保证操作的原子性;rt_thread_suspend和rt_thread_resume分别用于挂起和恢复线程。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值