20 同步与互斥(六)semaphore

发布时间 2023-04-29 20:33:26作者: 人民广场的二道贩子

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);										// 唤醒得到的信号量进程
}