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