linux -信号量分析
- 1 struct semaphore和sema_init
- 1.1 struct semaphore
- 1.2 sema_init
- 2 down
- 3 up
- 4 down_interruptible
- 5 down_killable
- 6 down_timeout
- 7 down_trylock
基于linux-5.15分析,信号量在使用是是基于spin lock封装实现的。
1 struct semaphore和sema_init
1.1 struct semaphore
/* Please don’t access any members of this structure directly */
struct semaphore {
raw_spinlock_t lock;
unsigned int count;
struct list_head wait_list;
};
1.2 sema_init
#define __SEMAPHORE_INITIALIZER(name, n) \
{ \.lock = __RAW_SPIN_LOCK_UNLOCKED((name).lock), \.count = n, \.wait_list = LIST_HEAD_INIT((name).wait_list), \
}#define DEFINE_SEMAPHORE(name) \struct semaphore name = __SEMAPHORE_INITIALIZER(name, 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);
}
2 down
static inline int __sched __down_common(struct semaphore *sem, long state,long timeout)
{struct semaphore_waiter waiter;list_add_tail(&waiter.list, &sem->wait_list);waiter.task = current;waiter.up = false;for (;;) {if (signal_pending_state(state, current)) goto interrupted;if (unlikely(timeout <= 0))goto timed_out;__set_current_state(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;
}static noinline void __sched __down(struct semaphore *sem)
{ __down_common(sem, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
}/*** down - acquire the semaphore* @sem: the semaphore to be acquired** Acquires the semaphore. If no more tasks are allowed to acquire the* semaphore, calling this function will put the task to sleep until the* semaphore is released.** Use of this function is deprecated, please use down_interruptible() or* down_killable() instead.*/
void down(struct semaphore *sem)
{unsigned long flags;might_sleep();raw_spin_lock_irqsave(&sem->lock, flags);if (likely(sem->count > 0))sem->count--;else __down(sem);raw_spin_unlock_irqrestore(&sem->lock, flags);
}
3 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->up = true; wake_up_process(waiter->task);
}/** * up - release the semaphore* @sem: the semaphore to release** Release the semaphore. Unlike mutexes, up() may be called from any* context and even by tasks which have never called down().*/
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);
}
4 down_interruptible
static noinline int __sched __down_interruptible(struct semaphore *sem)
{ return __down_common(sem, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
}/** * down_interruptible - acquire the semaphore unless interrupted* @sem: the semaphore to be acquired** Attempts to acquire the semaphore. If no more tasks are allowed to* acquire the semaphore, calling this function will put the task to sleep.* If the sleep is interrupted by a signal, this function will return -EINTR.* If the semaphore is successfully acquired, this function returns 0.*/
int down_interruptible(struct semaphore *sem)
{unsigned long flags;int result = 0;might_sleep();raw_spin_lock_irqsave(&sem->lock, flags);if (likely(sem->count > 0)) sem->count--;elseresult = __down_interruptible(sem);raw_spin_unlock_irqrestore(&sem->lock, flags);return result;
}
5 down_killable
static noinline int __sched __down_killable(struct semaphore *sem)
{return __down_common(sem, TASK_KILLABLE, MAX_SCHEDULE_TIMEOUT);
}/** * down_killable - acquire the semaphore unless killed* @sem: the semaphore to be acquired** Attempts to acquire the semaphore. If no more tasks are allowed to* acquire the semaphore, calling this function will put the task to sleep.* If the sleep is interrupted by a fatal signal, this function will return* -EINTR. If the semaphore is successfully acquired, this function returns* 0. */
int down_killable(struct semaphore *sem)
{unsigned long flags;int result = 0;might_sleep();raw_spin_lock_irqsave(&sem->lock, flags);if (likely(sem->count > 0)) sem->count--;elseresult = __down_killable(sem); raw_spin_unlock_irqrestore(&sem->lock, flags);return result;
}
6 down_timeout
static noinline int __sched __down_timeout(struct semaphore *sem, long timeout)
{return __down_common(sem, TASK_UNINTERRUPTIBLE, timeout);
}/*** down_timeout - acquire the semaphore within a specified time* @sem: the semaphore to be acquired* @timeout: how long to wait before failing** Attempts to acquire the semaphore. If no more tasks are allowed to* acquire the semaphore, calling this function will put the task to sleep.* If the semaphore is not released within the specified number of jiffies,* this function returns -ETIME. It returns 0 if the semaphore was acquired.*/
int down_timeout(struct semaphore *sem, long timeout)
{unsigned long flags; int result = 0;might_sleep();raw_spin_lock_irqsave(&sem->lock, flags);if (likely(sem->count > 0))sem->count--;elseresult = __down_timeout(sem, timeout);raw_spin_unlock_irqrestore(&sem->lock, flags);return result;
}
7 down_trylock
- count = sem->count - 1;
- likely(count >= 0)
- sem->count = count;
/*** down_trylock - try to acquire the semaphore, without waiting* @sem: the semaphore to be acquired** Try to acquire the semaphore atomically. Returns 0 if the semaphore has* been acquired successfully or 1 if it cannot be acquired.** NOTE: This return value is inverted from both spin_trylock and* mutex_trylock! Be careful about this when converting code.** Unlike mutex_trylock, this function can be used from interrupt context,* and the semaphore can be released by any task or interrupt.*/
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);
}