First, the kernel-related files are include/linux/semaphore.h and kernel/semaphore.c
Second, the main structural body:
struct semaphore {raw_spinlock_tlock;unsigned intcount;struct list_headwait_list;};
Structure member variable interpretation:
1, lock is mainly used to protect the count and wait_list linked list access;
2, Count records the semaphore wait process count;
3, Wait_list used to record the process of waiting for the semaphore, strung into a chain list to unified management;
Third, related interface
1. Initialize the interface
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 );}
The interface mainly completes the mutual exclusion lock, the initial value of the semaphore count, and the initial operation of the node of the chain header;
2, down signal volume interface
There are a total of five to get the semaphore interface:
extern void down (struct semaphore *sem), extern int __must_check down_interruptible (struct semaphore *sem); extern int __mu St_check down_killable (struct semaphore *sem); extern int __must_check down_trylock (struct semaphore *sem); extern int __ Must_check down_timeout (struct semaphore *sem, long jiffies);
1) down interface core is not recommended, according to the development trend of the kernel, will gradually cancel out this interface;
2) down signal interface, it is generally recommended to use down_interruptible, this interface will be the current task status set to Task_interruptible, meaning that once the condition is met, the task will be dispatched before the set timeout time arrives;
3) Down_killable: Can be killed to obtain the amount of signal. If sleep is interrupted by a fatal signal, return error-eintr;
4) Down_trylock: This interface comes up to get the semaphore, does not set the timeout time, does not do any wait, obtains to return 0, obtains not to return 1;
5) Down_timeout: If the semaphore is fetched up, it is returned directly, and the timeout time of the sleep wait setting is not obtained;
These five interfaces, in addition to the Down_trylock, eventually call the __down_common interface, but the parameters passed in are different.
static noinline void __sched __down (struct semaphore *sem) {__down_common (SEM, task_uninterruptible, Max_schedule_ TIMEOUT);} static noinline int __sched __down_interruptible (struct semaphore *sem) {return __down_common (SEM, task_interruptible, Max_schedule_timeout);} static noinline int __sched __down_killable (struct semaphore *sem) {return __down_common (SEM, task_killable, Max_ Schedule_timeout);} static noinline int __sched __down_timeout (struct semaphore *sem, long jiffies) {return __down_common (SEM, task_ uninterruptible, jiffies);}
We can take a look at __down_common this interface: First, the current process will be attached to the semaphore wait_list linked list, and then the other process through schedule_timeout scheduling, the current task hangs;
struct Semaphore_waiter {struct List_head list;struct task_struct *task;int up;};
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); waiter.task = task; Waiter.up = 0;for (;;) {if (Signal_pending_state (State, Task)) Goto interrupted;if (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; Interrupted:list_del (&waiter.list); return-eintr;}
3, the release of the signal interface: this interface mainly to do the following work, first determine whether the current semaphore wait_list is empty, if it is empty, that is, there is no process to wait for this semaphore, then direct sem->count++, otherwise, the first node in the wait_list removed, The wake_up_process is placed in the activation task queue and waits for execution.
extern void up (struct semaphore *sem), 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);} 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 = 1;wake_up_process ( Waiter->task);}
How semaphores work