semaphore

1 数据结构

struct semaphore {
	raw_spinlock_t		lock;
	unsigned int		count;
	struct list_head	wait_list;
};
typedef struct raw_spinlock {
	arch_spinlock_t raw_lock;
#ifdef CONFIG_GENERIC_LOCKBREAK
	unsigned int break_lock;
#endif
#ifdef CONFIG_DEBUG_SPINLOCK
	unsigned int magic, owner_cpu;
	void *owner;
#endif
#ifdef CONFIG_DEBUG_LOCK_ALLOC
	struct lockdep_map dep_map;
#endif
} raw_spinlock_t;

2 初始化

2.1 动态

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);
//初始化一个lock的实例lock-class映射信息
}
#define __SEMAPHORE_INITIALIZER(name, n)				\
{									\
	.lock		= __RAW_SPIN_LOCK_UNLOCKED((name).lock),	\
	.count		= n,						\
	.wait_list	= LIST_HEAD_INIT((name).wait_list),		\
}
如果n为0,表示该信号量不可用;如果n>0,表示信号量可用。
#define __RAW_SPIN_LOCK_UNLOCKED(lockname)	\
	(raw_spinlock_t) __RAW_SPIN_LOCK_INITIALIZER(lockname)
#define __RAW_SPIN_LOCK_INITIALIZER(lockname)	\
	{					\
	.raw_lock = __ARCH_SPIN_LOCK_UNLOCKED,	\
	SPIN_DEBUG_INIT(lockname)		\
	SPIN_DEP_MAP_INIT(lockname) }
也是将next和owner字段初始化为0。
CONFIG_LOCKDEP是一个打开lock debug的宏。
arch/arm/Kconfig
config LOCKDEP_SUPPORT
bool
default y
lib/Kconfig.debug
config LOCKDEP
bool
depends on DEBUG_KERNEL && TRACE_IRQFLAGS_SUPPORT && STACKTRACE_SUPPORT && LOCKDEP_SUPPORT
select STACKTRACE
select FRAME_POINTER if !MIPS && !PPC && !ARM_UNWIND && !S390 && !MICROBLAZE
select KALLSYMS
select KALLSYMS_ALL

2.2 静态

#define DEFINE_SEMAPHORE(name)	\
	struct semaphore name = __SEMAPHORE_INITIALIZER(name, 1)

3 请求信号量

3.1 down()

不可被中断的请求。
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);
}
信号量依赖于自旋锁。count>0说明信号量可用;否则,TASK_UNINTERRUPTIBLE的schedule()。
__down(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);//加入sem的等待wait_list,sem up的时候,会遍历wait_list
	waiter.task = task;
	waiter.up = false;

	for (;;) {
//如果是可被中断的可能会goto interrupted,否则会schedule,直到waiter.up为true。
		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;
//可被中断的请求信号量,被信号中断后返回-EINTR
 interrupted:
	list_del(&waiter.list);
	return -EINTR;
}
static inline int signal_pending_state(long state, struct task_struct *p)
{
//不可被中断或者未设置TASK_WAKEKILL(可被致命信号kill)
	if (!(state & (TASK_INTERRUPTIBLE | TASK_WAKEKILL)))
		return 0;
//没有待处理的信号。TIF_SIGPENDING置位,表示该进程有信号需要处理
	if (!signal_pending(p))
		return 0;
/*设置了TASK_INTERRUPTIBLE,或者设置TASK_WAKEKILL,并且有待处理的信号就走到了这里。TASK_INTERRUPTIBLE表示可以被信号中断,设置了它,这里会返回非0值;只设置了TASK_WAKEKILL,如果检测到SIGKILL信号也会返回非0值。*/
	return (state & TASK_INTERRUPTIBLE) || __fatal_signal_pending(p);
}

3.2 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;
}
__down_interruptible()-> __down_common(sem, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT)
请求成功返回0,如果没有信号将其中断,会schedule(),一直睡眠下去;如果睡眠被信号中断,会返回-EINTR。

3.3 down_killable()

可被kill的请求。
int down_killable(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_killable(sem);
	raw_spin_unlock_irqrestore(&sem->lock, flags);

	return result;
}
static noinline int __sched __down_killable(struct semaphore *sem)
{
	return __down_common(sem, TASK_KILLABLE, MAX_SCHEDULE_TIMEOUT);
}
task标志为
#define TASK_KILLABLE (TASK_WAKEKILL | TASK_UNINTERRUPTIBLE)
不可被中断的请求,并且可被致命信号kill。请求成功返回0;睡眠被致命信号中断,返回-EINTR。

3.4 down_trylock()

尝试原子的请求信号量。
int down_trylock(struct semaphore *sem)
{
	unsigned long flags;
	int count;

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

	return (count < 0);
}
如果请求成功返回0,不能请求到返回1。请求过程不会睡眠。
注意:spin_trylock and mutex_trylock的返回值是反转的。
mutex_trylock可以用于中断上下文,semaphore可用于任何进程和中断中。

3.5 down_timeout()

超时请求。
int down_timeout(struct semaphore *sem, long jiffies)
{
	unsigned long flags;
	int result = 0;

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

	return result;
}
int down_timeout(struct semaphore *sem, long jiffies)
{
	unsigned long flags;
	int result = 0;

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

	return result;
}
static noinline int __sched __down_timeout(struct semaphore *sem, long jiffies)
{
	return __down_common(sem, TASK_UNINTERRUPTIBLE, jiffies);
}
之前的几种请求,如果不填这个超时时间,都是用MAX_SCHEDULE_TIMEOUT。该时间指定了请求信号量的超时时间。请求成功返回0,如超时未获取信号量,返回-ETIME。

4 释放信号量

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);
}
如果wait_list为NULL,count++释放信号量;否则,不必释放信号量,直接移交给其他的task;需要处理wait_list。
static noinline void __sched __up(struct semaphore *sem)
{
	struct semaphore_waiter *waiter = list_first_entry(&sem->wait_list,
						struct semaphore_waiter, list); //找到第一个waiter
	list_del(&waiter->list);
	waiter->up = true;
	wake_up_process(waiter->task);
}

你可能感兴趣的:(linux-lock)