Linux 互斥体结构跟踪互斥体的当前所有者(如果有):
struct mutex {
atomic_long_t owner;
// ...
还有一个结构可以跟踪其他任务正在等待互斥锁:
/*
* This is the control structure for tasks blocked on mutex,
* which resides on the blocked task's kernel stack:
*/
struct mutex_waiter {
struct list_head list;
struct task_struct *task;
struct ww_acquire_ctx *ww_ctx;
#ifdef CONFIG_DEBUG_MUTEXES
void *magic;
#endif
};
相当简单,当你解锁一个互斥体时,内核会查看其他任务在那个互斥体上等待。它选择其中一个成为所有者,设置互斥锁的所有者字段以引用所选任务,然后从等待互斥锁的任务列表中删除该任务。到那时,任务至少很有可能已被解除阻塞,在这种情况下,一旦解除阻塞,它就可以运行了。此时,由调度程序决定何时运行它。
优化
由于互斥体被大量使用,并且它们被锁定和解锁的次数很多,因此它们使用了一些优化来帮助提高速度。例如,考虑以下情况:
/*
* @owner: contains: 'struct task_struct *' to the current lock owner,
* NULL means not owned. Since task_struct pointers are aligned at
* at least L1_CACHE_BYTES, we have low bits to store extra state.
*
* Bit0 indicates a non-empty waiter list; unlock must issue a wakeup.
* Bit1 indicates unlock needs to hand the lock to the top-waiter
* Bit2 indicates handoff has been done and we're waiting for pickup.
*/
#define MUTEX_FLAG_WAITERS 0x01
#define MUTEX_FLAG_HANDOFF 0x02
#define MUTEX_FLAG_PICKUP 0x04
#define MUTEX_FLAGS 0x07
因此,当您要求内核解锁互斥锁时,它可以“浏览”所有者指针中的某一位以确定这是否是“简单”情况(没有人在等待互斥锁,所以只需将其标记为解锁,然后出发),或者更复杂的任务(至少有一个任务在互斥体上等待,因此需要选择一个任务来解除阻塞,并将其标记为互斥体的新所有者。
参考文献
https://github.com/torvalds/linux/blob/master/include/linux/mutex.h
https://github.com/torvalds/linux/blob/master/kernel/locking/mutex.c
免责声明
在我写这个答案时,上面的代码摘录(我相信)是最新的。但如上所述,互斥锁被大量使用。如果您在 5 或 10 年后查看互斥锁的代码,您可能会发现有人在优化代码方面做了一些工作,因此它可能与我上面引用的内容不完全匹配。大多数概念可能保持相似,但细节(尤其是优化)的变化是意料之中的。