20 同步与互斥(六)semaphore
1 简介
semaphore信号量;需要注意的是信号量不是信号。
信号量是一种同步、互斥机制
2 semaphore的结构和API
2.1 semaphore结构
struct semaphore sem;
struct semaphore {
raw_spinlock_t lock; // semaphore借助与spinlock实现
unsigned int count; // 允许多少人使用这个semaphore
struct list_head wait_list; // 等待信号量的线程
};
2.2 semaphore API
-
DEFINE_SEMAPHORE(name)
定义一个struct semaphore name结构体count值为1
-
void sema_init(struct semaphore *sem, init val)
初始化semaphore。需要注意的是wait_list在这里被初始化了,休眠时将内核申请到的wait线程放入此
#define __SEMAPHORE_INITIALIZER(name, n) \ { \ .lock = __RAW_SPIN_LOCK_UNLOCKED((name).lock), \ .count = n, \ .wait_list = LIST_HEAD_INIT((name).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); }
-
void down(struct semaphore *sem)
获得信号量,如果无法获得就会休眠。休眠过程中无法被唤醒
-
int down_interruptible(struct semaphore *sem)
获得信号量,如果无法获得就会休眠。休眠过程中会被信号唤醒
return
- 0:获得信号量
- -EINTR:被信号打断
-
int down_killable(struct semaphore)
获得信号量,如果无法获得就会休眠。休眠过程中除了信号量就只有
fatal signal
信号唤醒能唤醒return
- 0:获得信号量
- -EINTR:被信号打断
-
int down_trylock(struct semaphore *sem)
尝试获取信号量,不会休眠
return
- 0:获得了信号量
- 1:没有获得信号量
-
int down_timeout(struct semaphore *sem, long jiffies)
获取信号量,如果不成功则休眠一段时间
return
- 0:获得信号量
- -EINTR:规定时间内未获得信号量
-
void up(struct semaphore *sem)
释放信号量,唤醒其他等待信号量的进程
3 semaphore 实现机制
3.1 信号量的获取
down -> __down -> __down_common
void down(struct semaphore *sem)
{
unsigned long flags;
raw_spin_lock_irqsave(&sem->lock, flags); // 上锁,互斥的访问count
if (likely(sem->count > 0))
sem->count--; // 如果count大于0,就减减,成功获取信号量。
else
__down(sem); // 信号量个数使用完,无法获取信号量,休眠
raw_spin_unlock_irqrestore(&sem->lock, flags); // 解锁,释放自旋锁
}
EXPORT_SYMBOL(down);
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); // 等待进程放入信号量的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); // 修改进程状态为非running
raw_spin_unlock_irq(&sem->lock); // 解锁
timeout = schedule_timeout(timeout); // 让出CPU, 开始调度
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;
}
3.2 3.1 信号量的释放
up -> __up
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);
}
EXPORT_SYMBOL(up);
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->list中删除
waiter->up = true; // 设置标记,表示此等待函数获得信号量
wake_up_process(waiter->task); // 唤醒得到的信号量进程
}