OK6410A 开发板 (八) 74 linux-5.11 OK6410A linux 内核同步机制 信号量(count=1)的实现

  • 解决的问题是什么
所有异常原因
  • 限制是什么
加锁函数会引起睡眠,所以不能...

实现

https://elixir.bootlin.com/linux/v4.0/source/kernel/locking/semaphore.c#L53
/*
向下-获取信号量
@sem:要获取的信号量
获取信号量。如果不允许更多任务获取信号量,则调用此函数将使任务进入睡眠状态,直到释放信号量。
不赞成使用此函数,请改用down_interruptible()或down_killable()。
 */
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);
}
EXPORT_SYMBOL(down);

https://elixir.bootlin.com/linux/v4.0/source/include/linux/spinlock.h#L215
#define raw_spin_lock_irqsave(lock, flags)			\
	do {						\
		typecheck(unsigned long, flags);	\
		flags = _raw_spin_lock_irqsave(lock);	\
	} while (0)

https://elixir.bootlin.com/linux/v4.0/source/kernel/locking/spinlock.c#L157
#ifndef CONFIG_INLINE_SPIN_LOCK_IRQSAVE
unsigned long __lockfunc _raw_spin_lock_irqsave(raw_spinlock_t *lock)
{
	return __raw_spin_lock_irqsave(lock);
}
EXPORT_SYMBOL(_raw_spin_lock_irqsave);
#endif

https://elixir.bootlin.com/linux/v4.0/source/include/linux/spinlock_api_smp.h#L106

static inline unsigned long __raw_spin_lock_irqsave(raw_spinlock_t *lock)
{
	unsigned long flags;

	local_irq_save(flags);
	preempt_disable();
	spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
	/*
	 * On lockdep we dont want the hand-coded irq-enable of
	 * do_raw_spin_lock_flags() code, because lockdep assumes
	 * that interrupts are not re-enabled during lock-acquire:
	 */
#ifdef CONFIG_LOCKDEP
	LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);
#else
	do_raw_spin_lock_flags(lock, &flags);
#endif
	return flags;
}


https://elixir.bootlin.com/linux/v4.0/source/include/linux/spinlock.h#L151
static inline void do_raw_spin_lock(raw_spinlock_t *lock) __acquires(lock)
{
	__acquire(lock);
	arch_spin_lock(&lock->raw_lock);
}

https://elixir.bootlin.com/linux/v4.0/source/arch/arm/include/asm/spinlock.h#L58
static inline void arch_spin_lock(arch_spinlock_t *lock)
{
	unsigned long tmp;
	u32 newval;
	arch_spinlock_t lockval;

	prefetchw(&lock->slock);
	__asm__ __volatile__(
"1:	ldrex	%0, [%3]\n"
"	add	%1, %0, %4\n"
"	strex	%2, %1, [%3]\n"
"	teq	%2, #0\n"
"	bne	1b"
	: "=&r" (lockval), "=&r" (newval), "=&r" (tmp)
	: "r" (&lock->slock), "I" (1 << TICKET_SHIFT)
	: "cc");

	while (lockval.tickets.next != lockval.tickets.owner) {
		wfe();
		lockval.tickets.owner = ACCESS_ONCE(lock->tickets.owner);
	}

	smp_mb();
}














https://elixir.bootlin.com/linux/v4.0/source/kernel/locking/semaphore.c#L236

static noinline void __sched __down(struct semaphore *sem)
{
	__down_common(sem, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
}
https://elixir.bootlin.com/linux/v4.0/source/kernel/locking/semaphore.c#L204
/*
因为这个函数是内联的,所以'state'参数将是常量,因此由编译器优化。同样,对于没有超时的情况,“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);
		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;
}
上一篇:linux kernel的spin_lock的详细介绍(以arm64为例)


下一篇:Java面试之基础篇(4)