crash调试技巧

张开发
2026/4/5 23:44:54 15 分钟阅读

分享文章

crash调试技巧
crash 判断互锁第一步定位阻塞进程死锁进程通常处于D (UNINTERRUPTIBLE)状态。# 列出所有D状态进程 crash foreach UN ps或crash ps | grep UN关键特征大量进程长时间卡在D状态WCHAN 列显示mutex_lock、rwsem_down_*、spin_*等锁相关函数第二步分析单个阻塞进程栈选一个 D 状态 PID查看调用栈crash bt PID锁等待典型栈帧mutex_lock/__mutex_lock_slowpathdown_read/rwsem_down_read_slowpathdown_write/rwsem_down_write_slowpathspin_lock_bug自旋锁死锁 / 误用示例栈PID: 1234 TASK: ffff99... CPU: 0 COMMAND: app #0 __schedule #1 schedule #2 schedule_preempt_disabled #3 __mutex_lock_slowpath #4 mutex_lock #5 func_a→ 说明进程在func_a中调用mutex_lock阻塞。第三步找到 “正在等哪个锁”方法 A从栈 / 寄存器提取锁地址定位mutex_lock所在栈帧查看传入的锁指针通常在x0/rdi或栈上# 反汇编查看参数 crash dis func_a ... 0xffff0000... mutex_lock: ... x0 锁地址方法 B直接查看 task_struct → pi_blocked_on /blocked_onmutex / rt_mutexcrash struct task_struct.pi_blocked_on task_addrsemaphore / rw_semaphorecrash struct task_struct.blocked_on task_addr得到锁结构体地址如mutex_addr。第四步查看锁的持有者1. mutex 查看crash mutex mutex_addr输出示例Mutex: ffff99... Owner: ffff99... (PID 5678) ← 持有锁的进程 Count: -1 (locked) WaitList: ...→ 记录持有者 PID 5678。2. rw_semaphore (读写锁)crash struct rw_semaphore rw_sem_addr关键字段owner/owner_task写锁持有者readers读计数wait_list等待队列3. spinlock自旋锁死锁crash spinlock spin_addrowner/owner_cpu持有 CPU / 进程若owner无效但locked可能是中断屏蔽死锁。第五步追溯闭环判断互锁进程 APID1234等锁 L1锁 L1 被进程 BPID5678持有对PID5678重复步骤二四crash bt 5678发现进程 B 也在 D 状态等锁 L2查锁 L2 → 持有者是进程 A→A 等 B、B 等 A → 确认互锁死锁。典型闭环A 持 L1 等 L2B 持 L2 等 L1或更长链A→B→C→A。crash 锁分析常用命令速查# 全局锁状态 crash lock # 系统所有锁概要 crash mutex -a # 所有mutex crash rwsem -a # 所有读写锁 # 进程与锁关联 crash task -l PID # 查看进程持有的锁 crash bt -a # 所有CPU栈找全局阻塞点 # 搜索锁地址定位所有等锁/持锁线程 crash search -t 锁地址 # 全任务栈搜索该指针快速判断互锁流程foreach UN ps→ 找 D 进程bt PID→ 定位锁函数mutex/rwsem addr→ 找持有者对持有者重复 2–3 步出现循环等待 → 确认互锁I/O D 状态WCHAN 为io_schedule、blk_mq等 → 不是锁死锁孤儿锁持有者已退出但锁未释放 → 非互锁软锁 / CPU stucksoft lockup多为自旋锁死循环快速定位系统卡死原因1. 看是否大量 D 状态不可中断ps | grep UN foreach UN psD 状态 阻塞在锁、IO、信号量全部 D → 大概率全局锁死锁 / 磁盘卡死2. 看所有 CPU 在干嘛bt -a所有 CPU 都卡在schedule/mutex_lock→死锁都卡在spin_lock/raw_spin_lock→自旋锁死锁硬死锁都卡在io_schedule→IO 堵死锁 / 死锁 / 互锁 专业技巧1. 直接看系统所有锁lock mutex -a rwsem -a spinlock -a2. 看进程在等哪个锁struct task_struct.pi_blocked_on 任务地址 struct task_struct.blocked_on 任务地址3. 看 mutex 持有者判断死锁核心mutex 锁地址看owner字段顺着等待链追A 等 B → B 等 C → C 等 A 互锁死锁4. 快速找死锁闭环技巧对每个 D 进程bt pid → 找到lock地址 → mutex addr → 得到owner pid重复 3 次基本就能看到环。panic 现场定位技巧1. 直接看崩溃点log bt最后一条日志就是触发 panic 的地方。2. 看寄存器regrip/pc崩溃指令地址rdi/rsi/rdx函数参数x86x0-x7函数参数arm643. 反汇编崩溃点dis *$rip内存问题排查OOM / 越界 / 空指针kmem -i # 内存总览 kmem -s # slab信息 vm # 虚拟地址布局 pte 虚拟地址 # 查页表 struct page 地址 # 页面信息空指针判断访问0x0附近一定是NULL 指针进程信息深度技巧task pid # 完整task_struct task -l pid # 进程持有的锁 files pid # 打开文件 fd pid # 文件描述符 vm pid # 进程虚拟内存CPU 卡死 / 软锁硬锁bt -a # 看所有CPU停在哪 runq # 运行队列 irq # 中断信息某个 CPU 一直spin_lock→自旋锁死循环所有 CPU 无法调度 →rcu_lock / 全局锁卡住最经典排查套路bt -a→ 看所有 CPU 堵在哪ps | grep UN→ 找 D 进程bt pid→ 看锁类型mutex addr→ 追 owner →找环系统卡死的 4 大类本质自旋锁死锁hard lockupCPU 空转死循环无法调度互斥锁死锁mutex deadlock进程互相等待形成闭环RCU 锁死某个 CPU 不响应 RCU全局阻塞IO 堵死磁盘 / 驱动卡死所有进程 D 状态第一步万能开局bt -a # 所有CPU的栈最重要 ps | grep UN # 所有D状态进程 log # 看内核日志是否有 hung/rcu/lockup看 bt -a 立刻判断死锁类型所有 CPU 都在 spin_lock /raw_spin_lock → 自旋锁硬死锁大部分 CPU 在 schedule /mutex_lock → 互斥锁死锁某个 CPU 在 rcu_node - 等待其他 CPU → RCU 死锁全部在 io_schedule /blk_mq - IO 堵死这一步90% 问题直接定位类型。第二类自旋锁硬死锁特征系统完全不响应不 panic无任何输出bt -a 看到 CPU 卡在native_queued_spin_lock_slowpath _raw_spin_lock定位方法看哪个 CPU 卡在 spin_lock看栈上锁地址从反汇编或栈偏移找查看锁持有者spinlock 锁地址看owner_cpuowner如果CPU0 持有锁CPU1 等锁CPU0 又在等其他锁 → 形成环 自旋锁互锁第三类mutex 互锁死锁特征大量 D 状态进程bt 栈__mutex_lock_slowpath mutex_lock排查链条固定套路bt pid # 找到等锁位置 dis 函数地址 # 找到锁的地址 mutex 锁地址 # 看 owner 进程A 等 BB 等 CC 等 A → 互锁确认快捷命令task -l pid # 显示进程持有的锁 mutex -a # 系统所有 mutex第四类RCU 锁死非常隐蔽的系统卡死特征log 出现RCU CPU stall rcu_sched detected stallsbt -a 看到很多进程卡在rcu_wait rcu_node synchronize_rcu定位rcuinfo rcu找到不响应 RCU 的 CPU然后bt CPU看它卡在什么锁上。RCU 死锁本质某个 CPU 被锁独占不响应时钟中断导致全局 RCU 卡住。第五类IO 堵死不是锁但表现像系统死特征所有进程 D 状态栈io_schedule blk_mq_delay_run_hw_queues scsi_queue_rq命令dev disk iostat定位故障盘或驱动卡死。如何在 crash 里快速找互锁环给你一个工程师内部固定流程1 分钟找到环ps | grep UN任选一个 D 进程bt pid找到等锁mutex addr得到 owner PIDbt owner_pid看它在等哪个锁重复直到回到最初 PID → 环形成只要出现环就是互锁死锁。

更多文章