Linux 6.6内核并发与同步(五):mutex 可睡眠锁

引言:spinlock 不能睡,mutex 能睡------怎么"能睡"还防饿死

上一篇 #4 讲了 qspinlock:等待者忙自旋 ,所以临界区必须极短、绝不能 sleep。但内核里大量锁的临界区没那么短 (比如操作一个链表、调一个会 sleep 的函数),忙等会白白烧 CPU。mutex(互斥锁) 就是"能 sleep 的锁":抢不到时,等待者先乐观自旋一小会 (万一 owner 马上就释放),还抢不到就把自己放进睡眠队列、让出 CPU ,等 owner 释放时唤醒队头交接锁。

它和 qspinlock 一样解决"缓存乒乓"和"防饿死",但换成了睡眠版本:

这条链路:mutex_lock fast path 一次 CAS 抢锁 → 失败进 slow path:先乐观自旋 (osq 队列里 spin on owner)→ 还抢不到就入队睡眠 (wait_list)→ owner 释放时handoff 唤醒队头。


全景图

#mermaid-svg-ZxArsg7IRuh51wrf{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ZxArsg7IRuh51wrf .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ZxArsg7IRuh51wrf .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ZxArsg7IRuh51wrf .error-icon{fill:#552222;}#mermaid-svg-ZxArsg7IRuh51wrf .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ZxArsg7IRuh51wrf .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ZxArsg7IRuh51wrf .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ZxArsg7IRuh51wrf .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ZxArsg7IRuh51wrf .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ZxArsg7IRuh51wrf .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ZxArsg7IRuh51wrf .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ZxArsg7IRuh51wrf .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ZxArsg7IRuh51wrf .marker.cross{stroke:#333333;}#mermaid-svg-ZxArsg7IRuh51wrf svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ZxArsg7IRuh51wrf p{margin:0;}#mermaid-svg-ZxArsg7IRuh51wrf .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ZxArsg7IRuh51wrf .cluster-label text{fill:#333;}#mermaid-svg-ZxArsg7IRuh51wrf .cluster-label span{color:#333;}#mermaid-svg-ZxArsg7IRuh51wrf .cluster-label span p{background-color:transparent;}#mermaid-svg-ZxArsg7IRuh51wrf .label text,#mermaid-svg-ZxArsg7IRuh51wrf span{fill:#333;color:#333;}#mermaid-svg-ZxArsg7IRuh51wrf .node rect,#mermaid-svg-ZxArsg7IRuh51wrf .node circle,#mermaid-svg-ZxArsg7IRuh51wrf .node ellipse,#mermaid-svg-ZxArsg7IRuh51wrf .node polygon,#mermaid-svg-ZxArsg7IRuh51wrf .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ZxArsg7IRuh51wrf .rough-node .label text,#mermaid-svg-ZxArsg7IRuh51wrf .node .label text,#mermaid-svg-ZxArsg7IRuh51wrf .image-shape .label,#mermaid-svg-ZxArsg7IRuh51wrf .icon-shape .label{text-anchor:middle;}#mermaid-svg-ZxArsg7IRuh51wrf .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ZxArsg7IRuh51wrf .rough-node .label,#mermaid-svg-ZxArsg7IRuh51wrf .node .label,#mermaid-svg-ZxArsg7IRuh51wrf .image-shape .label,#mermaid-svg-ZxArsg7IRuh51wrf .icon-shape .label{text-align:center;}#mermaid-svg-ZxArsg7IRuh51wrf .node.clickable{cursor:pointer;}#mermaid-svg-ZxArsg7IRuh51wrf .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ZxArsg7IRuh51wrf .arrowheadPath{fill:#333333;}#mermaid-svg-ZxArsg7IRuh51wrf .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ZxArsg7IRuh51wrf .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ZxArsg7IRuh51wrf .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZxArsg7IRuh51wrf .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ZxArsg7IRuh51wrf .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZxArsg7IRuh51wrf .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ZxArsg7IRuh51wrf .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ZxArsg7IRuh51wrf .cluster text{fill:#333;}#mermaid-svg-ZxArsg7IRuh51wrf .cluster span{color:#333;}#mermaid-svg-ZxArsg7IRuh51wrf div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ZxArsg7IRuh51wrf .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ZxArsg7IRuh51wrf rect.text{fill:none;stroke-width:0;}#mermaid-svg-ZxArsg7IRuh51wrf .icon-shape,#mermaid-svg-ZxArsg7IRuh51wrf .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZxArsg7IRuh51wrf .icon-shape p,#mermaid-svg-ZxArsg7IRuh51wrf .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ZxArsg7IRuh51wrf .icon-shape .label rect,#mermaid-svg-ZxArsg7IRuh51wrf .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZxArsg7IRuh51wrf .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ZxArsg7IRuh51wrf .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ZxArsg7IRuh51wrf :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 成功(无竞争)
失败
是
否(owner 没在跑/要调度)
mutex_lock mutex.c:281

might_sleep + trylock_fast
fast path

CAS owner 0→current 成功?
拿到锁
__mutex_lock_common mutex.c:569

preempt_disable
乐观自旋

mutex_optimistic_spin :441
osq_lock 排队 spin on owner

owner 在别的 CPU 上跑才自旋
owner 释放了?
入队睡眠

__mutex_add_waiter :205 + schedule :679
睡在 wait_list 队尾

set_current_state(TASK_UNINTERRUPTIBLE)
unlock 唤醒队头

__mutex_handoff :231 直接交接 owner
mutex_unlock :537

fast path 清 owner / 唤醒队头

一句话:mutex_lock fast path 一次 CAS 抢锁 → 失败进 slow path:先 mutex_optimistic_spin 乐观自旋(osq 队列里 spin on owner,owner 在跑才等)→ 抢不到就 __mutex_add_waiter 入队 + schedule 睡眠 → owner 释放时 __mutex_handoff 直接交接给队头,防新 spinner 插队饿死。


一、struct mutex:owner 指针压缩 3 个标志位

c 复制代码
// struct mutex(include/linux/mutex.h:64)
struct mutex {
	atomic_long_t		owner;       // 持有者 task_struct *(低 3 位是标志)
	raw_spinlock_t		wait_lock;   // 保护 wait_list 的自旋锁
#ifdef CONFIG_MUTEX_SPIN_ON_OWNER
	struct optimistic_spin_queue osq;  // 乐观自旋者的 MCS 队列
#endif
	struct list_head	wait_list;   // 睡眠等待队列(FIFO)
	...
};

核心是 owner------一个 atomic_long_t,把持有者指针 和三个标志位压在一起:

c 复制代码
// kernel/locking/mutex.c:68-72
#define MUTEX_FLAG_WAITERS	0x01   // 有人在 wait_list 睡眠等待
#define MUTEX_FLAG_HANDOFF	0x02   // 锁要交接给队头(禁止新 spinner 抢)
#define MUTEX_FLAG_PICKUP	0x04   // handoff 已完成、等队头认领(队头认领时清掉)
#define MUTEX_FLAGS		0x07

static inline struct task_struct *__mutex_owner(struct mutex *lock)   // :79
{
	return (struct task_struct *)(atomic_long_read(&lock->owner) & ~MUTEX_FLAGS);
}

Why 指针 + 标志位压缩 :task_struct * 是 8 字节对齐的(低 3 位必然为 0),所以低 3 位可以免费塞 3 个标志位。这是和 #4 qspinlock 的 32 位字、#1 里 READ_ONCE 同类思路------用对齐的"空位"存状态,一个原子变量同时表达"谁持有 + 什么状态" 。这样"抢锁 / 交接 / 判等待者"都能用单次 atomic_long_cmpxchg 完成。


二、fast path:一次 CAS 抢锁

c 复制代码
// mutex_lock(kernel/locking/mutex.c:281)
void __sched mutex_lock(struct mutex *lock)
{
	might_sleep();                              // ① 检查不在原子上下文
	if (!__mutex_trylock_fast(lock))            // ② fast path
		__mutex_lock_slowpath(lock);            // ③ 失败进慢路径
}

// __mutex_trylock_fast(mutex.c:166)
static __always_inline bool __mutex_trylock_fast(struct mutex *lock)
{
	unsigned long curr = (unsigned long)current;
	unsigned long zero = 0UL;
	if (atomic_long_try_cmpxchg_acquire(&lock->owner, &zero, curr))   // 0 → current
		return true;
	return false;
}

无竞争时 owner 是 0,atomic_long_try_cmpxchg_acquire 把它从 0 改成 current(当前 task),拿到锁。和 qspinlock 的 fast path 完全同构------一次 CAS 抢锁,acquire 语义。

might_sleep()(:283)是 mutex 的"身份检查":它内部检查当前是否在原子上下文(关抢占/关中断/持有自旋锁),是的话就 WARN------因为 mutex 要能 sleep ,绝不能在不能 sleep 的上下文里调(这是 #3 的类①底层纪律,might_sleep 是运行时抓它的钩子)。


三、slow path 三级:乐观自旋 → 入队睡眠 → handoff 唤醒

抢锁失败,说明有人持有。__mutex_lock_common(mutex.c:569)按"owner 有没有在跑"决定自旋还是睡:

3.1 乐观自旋:owner 在别的 CPU 上跑,才值得等

c 复制代码
// __mutex_lock_common(mutex.c:569)------ 关键片段
	preempt_disable();                                       // :602

	if (__mutex_trylock(lock) ||                             // ① 再试一次
	    mutex_optimistic_spin(lock, ww_ctx, NULL)) {         // ② 乐观自旋(:606-607)
		/* got the lock, yay! */
		...
		preempt_enable();
		return 0;
	}

	raw_spin_lock(&lock->wait_lock);                         // ③ 自旋失败,拿 wait_lock 准备睡(:617)

mutex_optimistic_spin(:441)是"自旋"与"睡"的分水岭:

c 复制代码
// mutex_optimistic_spin(mutex.c:441)------ 关键片段
	if (!waiter) {
		if (!mutex_can_spin_on_owner(lock))      // ① owner 没在跑/要调度,别自旋
			goto fail;
		if (!osq_lock(&lock->osq))               // ② 进乐观自旋队列(MCS)
			goto fail;
	}

	for (;;) {
		owner = __mutex_trylock_or_owner(lock);  // ③ 试着抢锁,顺便拿到 owner
		if (!owner)
			break;                               // 抢到了!
		if (!mutex_spin_on_owner(lock, owner, ww_ctx, waiter))  // ④ spin on owner
			goto fail_unlock;
		cpu_relax();
	}
	...

关键判据在 mutex_spin_on_owner(:352):

c 复制代码
// mutex_spin_on_owner(mutex.c:352)------ spin 的退出条件
	while (__mutex_owner(lock) == owner) {
		barrier();
		if (!owner_on_cpu(owner) || need_resched()) {   // ① owner 不在 CPU 上跑 / 该调度了(:373)
			ret = false;
			break;                                       // 放弃自旋,去睡
		}
		cpu_relax();
	}

Why 乐观自旋的判据是"owner 在不在 CPU 上跑" :mutex 的临界区可能较长,owner 可能自己 sleep 或被抢占 。如果 owner_on_cpu(owner) 为真,说明 owner 正在某个 CPU 上执行 ,大概率很快释放锁------自旋值得;如果 owner 已经不在 CPU 上(被抢占/睡眠),它要等调度器 把它重新调度回来,自旋纯属浪费,还不如自己 sleep 把 CPU 让出来。这就是"乐观"二字的含义------赌临界区短、owner 在线,赌输了就睡,不硬扛。

3.2 osq 队列:防"stampede"(复用 MCS 思想)

多个 CPU 同时乐观自旋同一把锁,都会 spin 在 owner 字段上------又是缓存乒乓。osq_lock(osq_lock.c:90)是专门给乐观自旋者的 MCS 队列,让 spinners 排队、各自 spin 自己的节点:

c 复制代码
// struct optimistic_spin_node(include/linux/osq_lock.h:9)
struct optimistic_spin_node {
	struct optimistic_spin_node *next, *prev;
	int locked;   // 1 = 轮到自己自旋了
	int cpu;      // 编码的 CPU 号 +1
};

// osq_lock(kernel/locking/osq_lock.c:90)------ 关键片段
	node = this_cpu_ptr(&osq_node);
	node->locked = 0;  node->next = NULL;  node->cpu = curr;
	old = atomic_xchg(&lock->tail, curr);         // ① 发布"我是队尾"(:107)
	if (old == OSQ_UNLOCKED_VAL)
		return true;                               // 队列空,直接进自旋
	prev = decode_cpu(old);
	smp_wmb();
	WRITE_ONCE(prev->next, node);                  // ② 挂到前驱
	if (smp_cond_load_relaxed(&node->locked, VAL || need_resched() ||
				  vcpu_is_preempted(node_cpu(node->prev))))   // ③ spin 自己的节点(:143)
		return true;
	/* unqueue: 该调度了,把自己摘出队列去 sleep */

这和 #4 qspinlock 的 MCS 队列是同一套思想 :atomic_xchg 发布尾 + spin 自己的 node->locked + prev->next 链接,避免所有 spinner 挤在同一个 cache line 上。区别是 osq 的节点带了 prev(乐观自旋者可能在等锁途中被 need_resched 打断,需要把自己摘出队列去 sleep)。

3.3 入队睡眠:wait_list FIFO

乐观自旋失败(owner 没在跑 / 该调度了),等待者正式去睡:

c 复制代码
// __mutex_lock_common(mutex.c:569)------ 睡眠路径
	raw_spin_lock(&lock->wait_lock);                    // ① 保护 wait_list
	if (__mutex_trylock(lock))                          // ② 拿锁间隙再抢一次(:621)
		goto skip_wait;

	__mutex_add_waiter(lock, &waiter, &lock->wait_list);   // ③ 加进 FIFO 队尾(:637)
	set_current_state(state);                           // ④ 置 TASK_UNINTERRUPTIBLE(:648)

	for (;;) {
		if (__mutex_trylock(lock))                      // ⑤ 抢锁(含 handoff 交接)
			goto acquired;
		...
		raw_spin_unlock(&lock->wait_lock);
		schedule_preempt_disabled();                    // ⑥ 睡!(:679)

		first = __mutex_waiter_is_first(lock, &waiter); // ⑦ 醒来后看自己是不是队头
		set_current_state(state);
		if (__mutex_trylock_or_handoff(lock, first))    // ⑧ 队头抢锁/置 HANDOFF 请求交接(:689)
			break;
		if (first)
			mutex_optimistic_spin(lock, ww_ctx, &waiter);  // ⑨ 队头还可以再乐观自旋(:694)
		raw_spin_lock(&lock->wait_lock);
	}

__mutex_add_waiter(:205)把等待者加到 wait_list 队尾(list_add_tail,FIFO),如果是第一个等待者就置 MUTEX_FLAG_WAITERS 标志(:212),告诉 owner"有人在等,释放时别走 fast path 直接返回"。然后 set_current_state(TASK_UNINTERRUPTIBLE) + schedule_preempt_disabled()(:679)------任务睡眠,让出 CPU,这就是 mutex 和 spinlock 的本质区别。

3.4 handoff:防新 spinner 插队饿死

睡眠等待者醒来后,靠 __mutex_handoff(:231)拿到锁。释放侧:

c 复制代码
// __mutex_unlock_slowpath(mutex.c:901)------ 关键片段
	owner = atomic_long_read(&lock->owner);
	for (;;) {
		if (owner & MUTEX_FLAG_HANDOFF)        // ① 已声明交接,退出循环(:921)
			break;
		if (atomic_long_try_cmpxchg_release(&lock->owner, &owner, __owner_flags(owner))) {
			if (owner & MUTEX_FLAG_WAITERS)    // ② 有等待者,跳出走唤醒(:925)
				break;
			return;                            // ③ 无等待者,直接返回(fast unlock)
		}
	}
	raw_spin_lock(&lock->wait_lock);
	if (!list_empty(&lock->wait_list)) {
		next = list_first_entry(&lock->wait_list, struct mutex_waiter, list)->task;  // :936
		wake_q_add(&wake_q, next);             // ④ 唤醒队头(:943)
	}
	if (owner & MUTEX_FLAG_HANDOFF)
		__mutex_handoff(lock, next);           // ⑤ 直接把 owner 设给队头(:947)
	raw_spin_unlock(&lock->wait_lock);
	wake_up_q(&wake_q);

__mutex_handoff(:231)把 owner 直接设成队头 task、置 MUTEX_FLAG_PICKUP、清 HANDOFF------锁不经过"清空再抢"的窗口,直接交接 。交接窗口内 owner 不为 0,新来的乐观 spinner 抢锁必然失败(__mutex_trylock_common 里 task 非 0 就走 break 放弃,:112-123),队头等待者一定拿到锁。

Why HANDOFF 防饿死 :如果没有 handoff,unlock 清空 owner 后,一个刚到的乐观 spinner 可能抢在睡眠等待者醒来之前 把锁拿走,反复如此,睡眠等待者永远拿不到锁------饿死 。HANDOFF 解决:队头等待者醒来发现锁仍被持有(被 spinner 抢了),就置 HANDOFF 请求(__mutex_trylock_or_handoff),此后持有者释放时 __mutex_handoff 把 owner 直接交接给队头、新 spinner 抢不到------和 #4 qspinlock 的 handoff 是同一个防饿死思路。


四、unlock:fast path 清 owner / 唤醒队头

c 复制代码
// mutex_unlock(mutex.c:537)
void __sched mutex_unlock(struct mutex *lock)
{
	if (__mutex_unlock_fast(lock))     // ① 无等待者:直接清 owner
		return;
	__mutex_unlock_slowpath(lock, _RET_IP_);   // ② 有等待者:走 §3.4 唤醒队头
}

// __mutex_unlock_fast(mutex.c:177)
static __always_inline bool __mutex_unlock_fast(struct mutex *lock)
{
	unsigned long curr = (unsigned long)current;
	return atomic_long_try_cmpxchg_release(&lock->owner, &curr, 0UL);   // current → 0
}

无等待者时,atomic_long_try_cmpxchg_release 把 owner 从 current 改回 0(release 语义),一条 CAS 搞定。有等待者(WAITERS 标志)或 HANDOFF 时,走 §3.4 的 __mutex_unlock_slowpath 唤醒队头。


五、mutex vs spinlock 选型

spinlock(#4) mutex
等待者 忙自旋(烧 CPU) 先乐观自旋 → 睡眠(让出 CPU)
临界区 极短(纳秒~微秒) 可长(能 sleep、调 schedule)
中断上下文 ✅ 可用 ❌ 不能(要 sleep,might_sleep 检查)
owner 无(锁里只有位) task_struct 指针(能知道谁持有)
防饿死 handoff HANDOFF 标志
典型场景 驱动热路径、中断 handler 进程上下文、文件系统、子系统内部

选型规则 :临界区短、可能被中断上下文访问 → spinlock(spin_lock_irqsave);临界区长、需要 sleep、只在进程上下文 → mutex。中间地带靠 mutex 的乐观自旋兜住------短临界区时它自旋很快,长临界区时它睡了不浪费 CPU,是"两头兼顾"的自适应设计。


六、与前面各篇的关系 + 记住三点

6.1 与前面各篇的关系

篇 关系
#4 qspinlock 同构的两套设计:32 位字 vs owner 指针压缩、MCS 队列 vs osq、handoff 防饿死;区别只在"忙等" vs "睡眠"
#3 死锁与锁序 might_sleep() 抓"原子上下文里睡"(类①纪律);mutex 不能用于中断上下文
#1 原子操作 owner 的 CAS 抢锁 = cmpxchg_acquire/cmpxchg_release,osq 的 atomic_xchg 发布队尾

6.2 与 AI Infra 的关系

  • 驱动/文件系统:进程上下文里要 sleep 的锁用 mutex(如 GPU 驱动的 ioctl 路径、文件系统 inode 锁)。
  • 用户态对照 :pthread_mutex 在无竞争时用用户态 CAS(futex 快路径),竞争时进内核 futex 睡眠------正是 mutex"乐观自旋→睡眠"思想的用户态版本(接 #14 futex)。
  • osq 的 MCS 思想:SPDK/DPDK 用户态锁、无锁队列复用(接 #15)。

6.3 记住三点

  1. owner 指针压缩 3 个标志位 :struct mutex(mutex.h:64)的 owner 是 atomic_long_t------高 61 位存 task_struct *(8 字节对齐,低 3 位空),低 3 位存 WAITERS/HANDOFF/PICKUP(mutex.c:68-70)。抢锁/交接/判等待者全用单次 atomic_long_cmpxchg。

  2. slow path 三级 = 乐观自旋 → 入队睡眠 → handoff :mutex_lock(:281)→ __mutex_lock_common(:569)先 mutex_optimistic_spin(:441,osq 队列里 spin on owner,owner_on_cpu 才等、need_resched 就退)→ 抢不到 __mutex_add_waiter(:205)+ schedule(:679)睡眠 → 释放时 __mutex_handoff(:231)直接交接 owner 给队头。

  3. 乐观自旋的判据是"owner 在不在 CPU 上跑" :mutex_spin_on_owner(:352)里 !owner_on_cpu(owner) || need_resched() 就放弃自旋去睡------赌"临界区短、owner 在线",赌输了就睡不硬扛。选型:短临界区/中断上下文 → spinlock;长临界区/要 sleep → mutex。


附:本篇关键源码索引

符号 位置
struct mutex(owner/wait_lock/osq/wait_list) include/linux/mutex.h:64
MUTEX_FLAG_WAITERS/HANDOFF/PICKUP kernel/locking/mutex.c:68-70
__mutex_owner(owner & ~MUTEX_FLAGS 取持有者) kernel/locking/mutex.c:79
mutex_lock(might_sleep + trylock_fast) kernel/locking/mutex.c:281
__mutex_trylock_fast(CAS owner 0→current) kernel/locking/mutex.c:166
__mutex_trylock_common(trylock + handoff 逻辑) kernel/locking/mutex.c:103
__mutex_trylock / __mutex_trylock_or_handoff(队头抢锁/置 HANDOFF) kernel/locking/mutex.c:150 / :142
mutex_optimistic_spin(乐观自旋) kernel/locking/mutex.c:441
mutex_spin_on_owner(owner_on_cpu 判据) kernel/locking/mutex.c:352
mutex_can_spin_on_owner kernel/locking/mutex.c:392
__mutex_lock_common(慢路径核心) kernel/locking/mutex.c:569
__mutex_add_waiter / __mutex_waiter_is_first kernel/locking/mutex.c:205 / :195
mutex_unlock / __mutex_unlock_slowpath kernel/locking/mutex.c:537 / :901
__mutex_handoff(交接 owner 给队头) kernel/locking/mutex.c:231
struct optimistic_spin_node / optimistic_spin_queue include/linux/osq_lock.h:9 / :15
osq_lock / osq_unlock kernel/locking/osq_lock.c:90 / :207
相关推荐
莫晓燚1 小时前
双机部署若依 (RuoYi‑Vue) 传统环境部署---实战
linux·运维·数据库·redis·mysql·nginx·ruoyi
开开心心就好2 小时前
竖式除法出题工具,整除余数混合模式
运维·服务器·前端·javascript·ocr·perl·1024程序员节
领麦微红外2 小时前
红外测温传感器 赋能充电桩热安全与运维智能化
运维·安全·产品经理·智能硬件
CQU_JIAKE3 小时前
10.30【A】
linux
Zevalin爱灰灰3 小时前
Linux入门第二部分(ARM裸机篇) 第一章 开发环境搭建与了解
linux·arm开发·嵌入式
程序猿老A3 小时前
轻量应用服务器总是CPU 100%怎么办?
服务器
LHICOD4 小时前
Linux-2
java·linux·后端·jar·vmware·svm·vm
余额瞒着我当琳4 小时前
Linux初识:Linux的历史,计算机的发展脉络,发行版本,选购云服务器,安装Xshell,第一次连接,基础命令指南
linux·运维·服务器