信号量semaphore
信号量是一种允许进程进入睡眠的同步机制,信号量是一个计数器,支持两种原语即P 和V操作,也就是down 和up 操作,
/* Please don't access any members of this structure directly */ struct semaphore { raw_spinlock_t lock;//用于对count以及wait_list成员的保护 unsigned int count;//表示允许进入临界区的控制路径 struct list_head wait_list;//用于管理所有在该信号上睡眠的进程 }; #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); }
信号量的操作:
void down(struct semaphore *sem); int __must_check down_interruptible(struct semaphore *sem); int __must_check down_killable(struct semaphore *sem); int __must_check down_trylock(struct semaphore *sem); int __must_check down_timeout(struct semaphore *sem, long jiffies); void up(struct semaphore *sem); 1 /** 2 * down_interruptible - acquire the semaphore unless interrupted 3 * @sem: the semaphore to be acquired
4 * 5 * Attempts to acquire the semaphore. If no more tasks are allowed to 6 * acquire the semaphore, calling this function will put the task to sleep. 7 * If the sleep is interrupted by a signal, this function will return -EINTR. 8 * If the semaphore is successfully acquired, this function returns 0. 9 */ 10 int down_interruptible(struct semaphore *sem) 11 { 12 unsigned long flags; 13 int result = 0; 14 15 raw_spin_lock_irqsave(&sem->lock, flags); 16 if (likely(sem->count > 0)) 17 sem->count--; 18 else 19 result = __down_interruptible(sem); 20 raw_spin_unlock_irqrestore(&sem->lock, flags); 21 22 return result; 23 } 24 static noinline int __sched __down_interruptible(struct semaphore *sem) 25 { 26 return __down_common(sem, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT); 27 } 28 /* 29 * Because this function is inlined, the 'state' parameter will be 30 * constant, and thus optimised away by the compiler. Likewise the 31 * 'timeout' parameter for the cases without timeouts. 32 */ 33 static inline int __sched __down_common(struct semaphore *sem, long state, 34 long timeout) 35 { 36 struct task_struct *task = current; 37 struct semaphore_waiter waiter; 38 39 list_add_tail(&waiter.list, &sem->wait_list);//添加到末尾 fifo 先进先出 40 waiter.task = task; 41 waiter.up = false; 42 43 for (;;) { 44 if (signal_pending_state(state, task))//收到信号且不是sigkill 45 goto interrupted; 46 if (unlikely(timeout <= 0))//超时唤醒 47 goto timed_out; 48 __set_task_state(task, state); 49 raw_spin_unlock_irq(&sem->lock); 50 timeout = schedule_timeout(timeout); 51 raw_spin_lock_irq(&sem->lock); 52 if (waiter.up)//up 为true 表示被唤醒切获取到自旋锁
53 return 0; 54 } 55 56 timed_out: 57 list_del(&waiter.list); 58 return -ETIME; 59 60 interrupted: 61 list_del(&waiter.list); 62 return -EINTR; 63 }
可知16-19行为临界区,涉及到count 的计数 会使用spinlock 来保护,由于这些信号量可能在中断函数里面使用,所以需要关闭本地cpu 中断,这里采用raw_spin_lock_irqsave,如果count值小于0 表示当前进程无法获取该信号量使用down_interrupt来执行睡眠等待操作,
49-50行:主动让出cpu之前需要释放该锁,
1 /** 2 * up - release the semaphore 3 * @sem: the semaphore to release 4 * 5 * Release the semaphore. Unlike mutexes, up() may be called from any 6 * context and even by tasks which have never called down(). 7 */ 8 void up(struct semaphore *sem) 9 { 10 unsigned long flags; 11 12 raw_spin_lock_irqsave(&sem->lock, flags); 13 if (likely(list_empty(&sem->wait_list))) 14 sem->count++; 15 else 16 __up(sem); 17 raw_spin_unlock_irqrestore(&sem->lock, flags); 18 } 19 static noinline void __sched __up(struct semaphore *sem) 20 { 21 struct semaphore_waiter *waiter = list_first_entry(&sem->wait_list, 22 struct semaphore_waiter, list); 23 list_del(&waiter->list); 24 waiter->up = true; 25 wake_up_process(waiter->task); 26 } 27 /** 28 * wake_up_process - Wake up a specific process 29 * @p: The process to be woken up. 30 * 31 * Attempt to wake up the nominated process and move it to the set of runnable 32 * processes. 33 * 34 * Return: 1 if the process was woken up, 0 if it was already running. 35 * 36 * It may be assumed that this function implies a write memory barrier before 37 * changing the task state if and only if any tasks are woken up. 38 */ 39 int wake_up_process(struct task_struct *p) 40 { 41 return try_to_wake_up(p, TASK_NORMAL, 0); 42 }
可知:如果信号量上的等待队列为空,则说明没有进程等待该变量,那么只需要将count++,如果不为空则有进程在等待队列休眠,需要up叫醒他们。up函数中唤醒进程是先进先出。
MUTEX 互斥体
mutex是阻塞锁,当某线程无法获取互斥量时,该线程会被直接挂起,该线程不再消耗CPU时间,当其他线程释放互斥量后,操作系统会激活那个被挂起的线程,让其投入运行.
问题:mutex 互斥体解决了什么问题?,mutex 和信号量的区别?
信号量可以根据初始化的count值大小分为计数信号量和互斥信号量。mutex 相当于count计数等于1的信号量,为什么还有mutex呢?
猜测原因:应该是在锁竞争激烈的场景下,mutex 执行更快吧,
链接:https://www.zhihu.com/question/47704079/answer/216430116
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
A mutex
is essentially the same thing as a binary semaphore and sometimes uses
the same basic implementation. The differences between them are in how
they are used. While a binary semaphore may be used as a mutex, a mutex
is a more specific use-case, in that only the thread that locked the
mutex is supposed to unlock it. This constraint makes it possible to
implement some additional features in mutexes:
- Since only the thread that locked the mutex is supposed to unlock
it, a mutex may store the id of thread that locked it and verify the
same thread unlocks it. - Mutexes may provide priority inversion
safety. If the mutex knows who locked it and is supposed to unlock it,
it is possible to promote the priority of that thread whenever a
higher-priority task starts waiting on the mutex. - Mutexes may also provide deletion safety, where the thread holding the mutex cannot be accidentally deleted.
- Alternately, if the thread holding the mutex is deleted (perhaps due
to an unrecoverable error), the mutex can be automatically released. - A mutex may be recursive: a thread is allowed to lock it multiple times without causing a deadlock.
/* * Simple, straightforward mutexes with strict semantics: * * - only one task can hold the mutex at a time * - only the owner can unlock the mutex * - multiple unlocks are not permitted * - recursive locking is not permitted * - a mutex object must be initialized via the API * - a mutex object must not be initialized via memset or copying * - task may not exit with mutex held * - memory areas where held locks reside must not be freed * - held mutexes must not be reinitialized * - mutexes may not be used in hardware or software interrupt * contexts such as tasklets and timers * * These semantics are fully enforced when DEBUG_MUTEXES is * enabled. Furthermore, besides enforcing the above rules, the mutex * debugging code also implements a number of additional features * that make lock debugging easier and faster: * * - uses symbolic names of mutexes, whenever they are printed in debug output * - point-of-acquire tracking, symbolic lookup of function names * - list of all locks held in the system, printout of them * - owner tracking * - detects self-recursing locks and prints out all relevant info * - detects multi-task circular deadlocks and prints out all affected * locks and tasks (and only those tasks) */ struct mutex { /* 1: unlocked, 0: locked, negative: locked, possible waiters */ atomic_t count;//原子计数 1表示没有人持有锁,0 表示被持有 spinlock_t wait_lock; //用于保护wait_list睡眠等待队列 struct list_head wait_list; #if defined(CONFIG_DEBUG_MUTEXES) || defined(CONFIG_MUTEX_SPIN_ON_OWNER) struct task_struct *owner; #endif #ifdef CONFIG_MUTEX_SPIN_ON_OWNER struct optimistic_spin_queue osq; /* Spinner MCS lock */ #endif #ifdef CONFIG_DEBUG_MUTEXES void *magic; #endif #ifdef CONFIG_DEBUG_LOCK_ALLOC struct lockdep_map dep_map; #endif };
Spinner MCS lock : mcs 一种spinlock的优化方案
mutex 实现:
1 /** 2 * mutex_lock - acquire the mutex 3 * @lock: the mutex to be acquired 4 * 5 * Lock the mutex exclusively for this task. If the mutex is not 6 * available right now, it will sleep until it can get it. 7 * 8 * The mutex must later on be released by the same task that 9 * acquired it. Recursive locking is not allowed. The task 10 * may not exit without first unlocking the mutex. Also, kernel 11 * memory where the mutex resides must not be freed with 12 * the mutex still locked. The mutex must first be initialized 13 * (or statically defined) before it can be locked. memset()-ing 14 * the mutex to 0 is not allowed. 15 * 16 * ( The CONFIG_DEBUG_MUTEXES .config option turns on debugging 17 * checks that will enforce the restrictions and will also do 18 * deadlock debugging. ) 19 * 20 * This function is similar to (but not equivalent to) down(). 21 */ 22 void __sched mutex_lock(struct mutex *lock) 23 { 24 might_sleep(); 25 /* 26 * The locking fastpath is the 1->0 transition from 27 * 'unlocked' into 'locked' state. 28 */ 29 __mutex_fastpath_lock(&lock->count, __mutex_lock_slowpath); 30 mutex_set_owner(lock);//成功持有锁后 lock->owner 只想当前进程的task_struct 当前线程的thread_info可以通过sp低13位清零获取到
31 }
eg:如果count计数减1后小于0,表示该锁已经被人持有,这进入slowpath, 1 __mutex_lock_slowpath(atomic_t *lock_count)
2 { 3 struct mutex *lock = container_of(lock_count, struct mutex, count); 4 5 __mutex_lock_common(lock, TASK_UNINTERRUPTIBLE, 0, 6 NULL, _RET_IP_, NULL, 0); 7 } 8 9 /* 10 * Lock a mutex (possibly interruptible), slowpath: 11 */ 12 static __always_inline int __sched 13 __mutex_lock_common(struct mutex *lock, long state, unsigned int subclass, 14 struct lockdep_map *nest_lock, unsigned long ip, 15 struct ww_acquire_ctx *ww_ctx, const bool use_ww_ctx) 16 { 17 struct task_struct *task = current;// 通过sp低13位清零获取到thread_info,再通过thread_info获取到task指针
18 struct mutex_waiter waiter; 19 unsigned long flags; 20 int ret; 21 22 if (use_ww_ctx) { 23 struct ww_mutex *ww = container_of(lock, struct ww_mutex, base); 24 if (unlikely(ww_ctx == READ_ONCE(ww->ctx))) 25 return -EALREADY; 26 } 27 28 preempt_disable(); 29 mutex_acquire_nest(&lock->dep_map, subclass, 0, nest_lock, ip); 30 31 if (mutex_optimistic_spin(lock, ww_ctx, use_ww_ctx)) { 32 /* got the lock, yay! */ 33 preempt_enable(); 34 return 0; 35 } 36 37 spin_lock_mutex(&lock->wait_lock, flags);// 获取lock->wait_lock自旋锁,直到获取成功为止,因为接下来的代码要对lock的成员变量进行读写,需要互斥访问 38 39 /* 40 * Once more, try to acquire the lock. Only try-lock the mutex if 41 * it is unlocked to reduce unnecessary xchg() operations. 42 */ // 循环读取互斥锁计数器count的值,直到count为1,表示互斥锁可用,此处读取互斥锁计数器的同时也将互斥锁的值设置为0了 43 if (!mutex_is_locked(lock) && 44 (atomic_xchg_acquire(&lock->count, 0) == 1)) 45 goto skip_wait; 46 47 debug_mutex_lock_common(lock, &waiter); 48 debug_mutex_add_waiter(lock, &waiter, task_thread_info(task)); 49 50 /* add waiting tasks to the end of the waitqueue (FIFO): */ 51 list_add_tail(&waiter.list, &lock->wait_list); 52 waiter.task = task; 53 54 lock_contended(&lock->dep_map, ip); 55 56 for (;;) { 57 /* 58 * Lets try to take the lock again - this is needed even if 59 * we get here for the first time (shortly after failing to 60 * acquire the lock), to make sure that we get a wakeup once 61 * it's unlocked. Later on, if we sleep, this is the 62 * operation that gives us the lock. We xchg it to -1, so 63 * that when we release the lock, we properly wake up the 64 * other waiters. We only attempt the xchg if the count is 65 * non-negative in order to avoid unnecessary xchg operations: 66 */ 67 if (atomic_read(&lock->count) >= 0 && 68 (atomic_xchg_acquire(&lock->count, -1) == 1)) 69 break; 70 71 /* 72 * got a signal? (This code gets eliminated in the 73 * TASK_UNINTERRUPTIBLE case.) 74 */ 75 if (unlikely(signal_pending_state(state, task))) { 76 ret = -EINTR; 77 goto err; 78 } 79 80 if (use_ww_ctx && ww_ctx->acquired > 0) { 81 ret = __ww_mutex_lock_check_stamp(lock, ww_ctx); 82 if (ret) 83 goto err; 84 } 85 // 设置当前任务状态TASK_UNINTERRUPTIBLE
86 __set_task_state(task, state); 87 88 /* didn't get the lock, go to sleep: */ 89 spin_unlock_mutex(&lock->wait_lock, flags); // 释放自旋锁(此处对lock的成员变量修改读写都已经完成,其他任务可读写修改 90 schedule_preempt_disabled();/ 执行一次调度,主动切换到其他任务,等其他任务释放互斥锁是会唤醒当前任务,继续执行下面的函数 91 spin_lock_mutex(&lock->wait_lock, flags);// 任务被唤醒,重新获取自旋锁 92 } 93 __set_task_state(task, TASK_RUNNING); 94 95 mutex_remove_waiter(lock, &waiter, current_thread_info()); 96 /* set it to 0 if there are no waiters left: */ 97 if (likely(list_empty(&lock->wait_list))) 98 atomic_set(&lock->count, 0); 99 debug_mutex_free_waiter(&waiter); 100 101 skip_wait: 102 /* got the lock - cleanup and rejoice! */ 103 lock_acquired(&lock->dep_map, ip); 104 mutex_set_owner(lock); 105 106 if (use_ww_ctx) { 107 struct ww_mutex *ww = container_of(lock, struct ww_mutex, base); 108 ww_mutex_set_context_slowpath(ww, ww_ctx); 109 } 110 111 spin_unlock_mutex(&lock->wait_lock, flags); 112 preempt_enable(); 113 return 0; 114 115 err: 116 mutex_remove_waiter(lock, &waiter, task_thread_info(task)); 117 spin_unlock_mutex(&lock->wait_lock, flags); 118 debug_mutex_free_waiter(&waiter); 119 mutex_release(&lock->dep_map, 1, ip); 120 preempt_enable(); 121 return ret; 122 }
就不详细看了 ;
主要是mutex 和semaphore使用区别:一个是互斥一个 计数信号量
以生活现象为例:超市最多允许同时进100个人,一开始100人顺利进入,后面出一个进一个
// 通过sp低13位清零获取到thread_info,再通过thread_info获取到task指针