尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

Android 7系统异常问题排查(二)内核层—Kernel Panic与系统重启

Android 7系统异常问题排查(二)内核层—Kernel Panic与系统重启
📅 发布时间:2026/8/4 2:18:20

系列目录:第一篇:异常机制全景图 | 第二篇:Kernel Panic 与系统重启 | 第三篇:Tombstone 机制 | 第四篇:System Server Watchdog | 第五篇:System Server 崩溃 | 第六篇:ANR 机制 | 第七篇:Java 层崩溃 | 第八篇:Trace 机制 | 第九篇:日志系统 | 第十篇:实战方法论


一、为什么需要深入内核异常?

你可能遇到过这些棘手问题:

  • 设备突然黑屏重启,logcat 里什么都没有,只有/proc/last_kmsg留下一句Kernel panic - not syncing
  • 压力测试时系统随机重启,日志显示Watchdog detected hard LOCKUP on cpu 2
  • 充电时设备重启,内核日志显示是电源管理驱动触发了 panic
  • 内核日志显示BUG: soft lockup - CPU#1 stuck for 23s,但不知道是什么卡住了 CPU

这些问题都发生在 Linux 内核层,是 Android 系统最底层的异常。如果缺乏对内核异常机制的理解,面对last_kmsg时只会一头雾水。

本文基于AOSP 7(Linux 3.18)源码,深入分析内核层的两大异常机制:Kernel Panic 与内核 Watchdog,以及 panic 后的现场保存与重启流程。


二、Kernel Panic 的本质

Kernel Panic 是 Linux 内核在遇到无法安全继续运行的致命错误时,主动终止系统运行的行为。在 Android 设备上表现为:屏幕卡死 → 系统自动重启(如果panic_timeout > 0)。

触发 Kernel Panic 的典型场景

场景类型具体例子触发路径
硬件故障内存位翻转、CPU 过热、总线错误ARM 异常向量 →die()→panic()
内核 BUG空指针解引用、自旋锁死锁、栈溢出BUG()/BUG_ON()→panic()
驱动异常设备驱动访问非法地址、DMA 错误驱动中panic()调用
文件系统损坏关键文件系统元数据损坏,无法挂载mount()失败 →panic()
init 进程死亡init 进程(PID 1)意外退出kernel/exit.c中panic("Attempted to kill init!")
手动触发echo c > /proc/sysrq-triggerSysRq 触发 panic

三、panic() 函数源码分析

panic()位于kernel/panic.c,是内核 panic 的核心入口。理解其执行顺序对分析 panic 日志至关重要。

源码路径:kernel/msm-3.18/kernel/panic.c

voidpanic(constchar*fmt,...){staticDEFINE_SPINLOCK(panic_lock);staticcharbuf[1024];va_list args;longi,i_next=0;intstate=0;// 1. 禁用本地中断,防止死锁local_irq_disable();// 2. 获取 panic 锁,确保只有一个 CPU 执行 panicif(!spin_trylock(&panic_lock))panic_smp_self_stop();console_verbose();bust_spinlocks(1);va_start(args,fmt);vsnprintf(buf,sizeof(buf),fmt,args);va_end(args);// 3. 打印 panic 信息pr_emerg("Kernel panic - not syncing: %s\n",buf);// 4. 打印调用栈(如果配置了 CONFIG_DEBUG_BUGVERBOSE)if(!test_taint(TAINT_DIE)&&oops_in_progress<=1)dump_stack();// 5. 停止其他 CPU 核心smp_send_stop();// 6. 调用 panic 通知链atomic_notifier_call_chain(&panic_notifier_list,0,buf);// 7. 将内核日志写入 pstore/ramoopskmsg_dump(KMSG_DUMP_PANIC);// 8. 根据 panic_timeout 决定是否重启if(panic_timeout>0){pr_emerg("Rebooting in %d seconds..",panic_timeout);for(i=0;i<panic_timeout*1000;i+=PANIC_TIMER_STEP){touch_nmi_watchdog();mdelay(PANIC_TIMER_STEP);}}if(panic_timeout!=0){emergency_restart();}// 如果 panic_timeout == 0,无限循环等待for(i=0;;i+=PANIC_TIMER_STEP){touch_softlockup_watchdog();mdelay(PANIC_TIMER_STEP);}}

关键设计:panic() 的执行顺序是精心设计的——先打印日志(步骤 3-4),再停止其他 CPU(步骤 5)。如果先停 CPU,当前 CPU 可能无法输出日志。kmsg_dump()在smp_send_stop()之后执行,确保日志能写入 pstore。

panic() 执行流程图

panic(const char *fmt, ...) │ ├─ 1. local_irq_disable() — 禁用本地中断 ├─ 2. spin_trylock(&panic_lock) — 获取 panic 锁 ├─ 3. pr_emerg("Kernel panic...") — 打印 panic 信息 ├─ 4. dump_stack() — 打印调用栈 ├─ 5. smp_send_stop() — 停止其他 CPU 核心 ├─ 6. atomic_notifier_call_chain() — 调用 panic 通知链 ├─ 7. kmsg_dump(KMSG_DUMP_PANIC) — 日志写入 pstore/ramoops └─ 8. 根据 panic_timeout 决定行为 ├─ > 0: mdelay(panic_timeout*1000) → emergency_restart() ├─ = 0: 无限等待 (调试用) └─ < 0: 立即重启

关键参数:panic_timeout

panic_timeout控制 panic 后的行为,通过内核命令行或 sysctl 配置:

源码路径:kernel/msm-3.18/kernel/panic.c

// 默认值来自内核配置 CONFIG_PANIC_TIMEOUTintpanic_timeout=CONFIG_PANIC_TIMEOUT;EXPORT_SYMBOL_GPL(panic_timeout);// 内核命令行参数:panic=Ncore_param(panic,panic_timeout,int,0644);
# 内核命令行配置androidboot.panic_timeout=5# 5秒后重启# 运行时修改echo5>/proc/sys/kernel/panic
场景panic_timeout 值行为
开发阶段0无限等待,方便连接 JTAG 调试
量产固件55 秒后自动重启(高通平台默认值)
压力测试1快速重启,收集更多 panic 样本

关键设计:panic_timeout=0时系统会无限循环在for (i = 0; ; ...)中,不断调用touch_softlockup_watchdog()防止 watchdog 触发,让开发者有时间连接调试器。

smp_send_stop() —— 停止其他 CPU

当某个 CPU 触发 panic 后,必须立即停止其他所有 CPU,否则它们可能继续修改内存,破坏 crash dump 的准确性。

源码路径:kernel/msm-3.18/kernel/smp.c(架构相关实现)

// 简化的伪代码,实际实现在 arch/arm*/kernel/smp.cvoidsmp_send_stop(void){// 向其他 CPU 发送 IPI(核间中断)// 其他 CPU 收到 IPI 后执行 cpu_panic_stop()// cpu_panic_stop() 会无限循环,等待重启}

注意:如果其他 CPU 在关中断状态下死锁,IPI 无法到达——这就是"hard lockup"场景,需要 NMI(不可屏蔽中断)来处理。


四、内核 Watchdog:Hard Lockup 与 Soft Lockup

内核 Watchdog 用于检测 CPU 死锁,分为两类:Hard Lockup(硬死锁)和 Soft Lockup(软死锁)。

4.1 Hard Lockup Detector(硬死锁检测)

检测对象:CPU 在关中断状态下长时间无响应。

原理:利用NMI(不可屏蔽中断)——即使 CPU 关中断了,NMI 仍能到达。

源码路径:kernel/msm-3.18/kernel/watchdog.c

// Hard lockup 检测的核心逻辑(简化)staticintis_hardlockup(void){unsignedlonghrint=__this_cpu_read(hrtimer_interrupts);// 如果 hrtimer 中断计数没有更新,说明 CPU 死锁if(__this_cpu_read(hrtimer_interrupts_saved)==hrint)return1;__this_cpu_write(hrtimer_interrupts_saved,hrint);return0;}// NMI 处理函数staticvoidwatchdog_overflow_callback(structperf_event*event,...){if(is_hardlockup()){intthis_cpu=smp_processor_id();// 只打印一次if(__this_cpu_read(hard_watchdog_warn)==true)return;if(hardlockup_panic)panic("Watchdog detected hard LOCKUP on cpu %d",this_cpu);elseWARN(1,"Watchdog detected hard LOCKUP on cpu %d",this_cpu);__this_cpu_write(hard_watchdog_warn,true);}}
Hard Lockup 检测原理: 1. 每个 CPU 有一个 hrtimer(高精度定时器),每 4 秒触发一次 2. hrtimer 触发时递增 hrtimer_interrupts 计数器 3. NMI watchdog 通过 perf event 监控 CPU 周期 4. 如果 NMI 触发时发现 hrtimer_interrupts 没有更新 └─ 说明 hrtimer 被阻塞 → CPU 关中断死锁 → 触发 panic

日志特征:Kernel panic - not syncing: Watchdog detected hard LOCKUP on cpu 2

4.2 Soft Lockup Detector(软死锁检测)

检测对象:CPU 在开中断但长时间无法调度(自旋锁持有过久、长时间循环)。

原理:hrtimer 每 4 秒触发,检查 CPU 是否有调度事件发生。超过阈值(默认 20 秒)则触发软锁死告警。

源码路径:kernel/msm-3.18/kernel/watchdog.c

// Soft lockup 检测的核心逻辑staticintis_softlockup(unsignedlongtouch_ts){unsignedlongnow=get_timestamp();// 如果当前时间 - 上次更新时间 > 阈值(20秒)if(time_after(now,touch_ts+get_softlockup_thresh()))returnnow-touch_ts;// 返回死锁时长return0;}// hrtimer 处理函数staticenumhrtimer_restartwatchdog_timer_fn(structhrtimer*hrtimer){unsignedlongtouch_ts=__this_cpu_read(watchdog_touch_ts);intduration;// 检查 soft lockupduration=is_softlockup(touch_ts);if(unlikely(duration)){pr_emerg("BUG: soft lockup - CPU#%d stuck for %us! [%s:%d]\n",smp_processor_id(),duration,current->comm,task_pid_nr(current));dump_stack();if(softlockup_panic)panic("softlockup: hung tasks");}returnHRTIMER_RESTART;}
Soft Lockup 检测原理: 1. 每个 CPU 有一个 watchdog 内核线程 2. watchdog 线程定期调用 __touch_watchdog() 更新时间戳 3. hrtimer 每 4 秒检查一次时间戳 4. 如果时间戳超过 20 秒未更新 └─ 说明 watchdog 线程被阻塞 → CPU 无法调度 → 触发告警

日志特征:BUG: soft lockup - CPU#1 stuck for 23s! [swapper/1:0]

4.3 运行时控制

# 查看 watchdog 状态cat/proc/sys/kernel/watchdog# 0:禁用, 1:启用cat/proc/sys/kernel/watchdog_thresh# 默认 10 秒# 手动触发所有 CPU 的 backtrace(调试用)echo1>/proc/sys/kernel/softlockup_all_cpu_backtrace

五、重启流程与重启原因

5.1 emergency_restart() 调用链

panic 后最终调用emergency_restart()触发硬件重启:

源码路径:kernel/msm-3.18/kernel/reboot.c

voidemergency_restart(void){kmsg_dump(KMSG_DUMP_EMERG);machine_emergency_restart();}EXPORT_SYMBOL_GPL(emergency_restart);
panic() → emergency_restart() → machine_emergency_restart() └─ 架构实现(arch/arm*/kernel/reboot.c): ├─ 写入 PMIC 复位寄存器 ├─ 触发硬件看门狗后死循环等待复位 └─ 写 PS_HOLD(高通平台)

5.2 重启原因记录(Reboot Reason)

内核在 panic 时会将重启原因写入 PMIC 寄存器或 IMEM,BootLoader 读取后传递给内核命令行:

写入:内核写 reboot reason 到 PMIC 寄存器 / IMEM 读取:BootLoader → 内核命令行 androidboot.bootreason=kernel_panic 用户空间:/sys/kernel/boot_reason 或 ro.boot.bootreason

常见重启原因值:

值含义
kernel_panic内核 panic
watchdog硬件/内核 watchdog 超时
longkey长按电源键
recovery进入 recovery 模式
unknown未知原因

六、现场保存:pstore 与 ramoops

Kernel panic 发生后重启会导致所有内核日志(dmesg)丢失。pstore(Persistent Store)框架通过保留 DDR 区域来解决此问题。

6.1 原理

DDR 内存布局: ┌───────────────────────────────┐ │ 常规内存(重启后被清零) │ ├───────────────────────────────┤ │ ramoops 保留区域 │ ← 内核参数 mem= 保留 │ ├─ console-ramoops (控制台) │ │ ├─ pmsg-ramoops (用户态) │ │ └─ ftrace-ramoops (ftrace) │ └───────────────────────────────┘ 重启后 BootLoader 不会触碰这个区域

6.2 AOSP 7 中的挂载

源码路径:system/core/rootdir/init.rc

# init.rc 第 228-233 行 # pstore/ramoops previous console log mount pstore pstore /sys/fs/pstore chown system log /sys/fs/pstore/console-ramoops chmod 0440 /sys/fs/pstore/console-ramoops chown system log /sys/fs/pstore/pmsg-ramoops-0 chmod 0440 /sys/fs/pstore/pmsg-ramoops-0

注意:pstore 挂载在on init阶段(第 31 行开始),而非on post-fs。挂载后/sys/fs/pstore/console-ramoops即上次 panic 的内核日志。

6.3 平台差异

平台ramoops 配置方式last_kmsg 路径
高通 (Qualcomm)ramoops_memreserve=命令行/sys/fs/pstore/console-ramoops
联发科 (MTK)MTK 自定义 aee 框架/data/aee_exp/或/proc/last_kmsg
展讯 (Spreadtrum)展讯自定义 dump/data/log/dump/

七、last_kmsg 解读

典型的内核 panic 日志片段:

[ 1234.567890] Unable to handle kernel NULL pointer dereference at virtual address 00000000 [ 1234.567900] pgd = c0004000 [ 1234.567920] Internal error: Oops: 805 [#1] PREEMPT SMP ARM [ 1234.567930] CPU: 1 PID: 234 Comm: Binder:234_1 [ 1234.567950] PC is at my_function+0x18/0x50 [ 1234.567960] LR is at caller_function+0x2c/0x48 [ 1234.567980] [<c0123456>] (my_function) from [<c0234567>] (caller_function+0x2c/0x48) [ 1234.567990] [<c0234567>] (caller_function) from [<c0345678>] (top_function+0x14/0x2c)

关键解读点:

行含义
Unable to handle kernel NULL pointer dereference错误类型:空指针解引用
at virtual address 00000000访问的地址为 0x0
Internal error: Oops: 805Oops 错误码
PC is at my_function+0x18/0x50PC 位置(偏移 0x18,函数总长 0x50)
CPU: 1 PID: 234出问题的 CPU 和进程
Backtrace:内核调用栈回溯

八、定位工具与技巧

8.1 快速抓取 last_kmsg

# 方法1:直接从 /proc 读取adb shellcat/proc/last_kmsg>last_kmsg.txt# 方法2:从 pstore 读取adb shellcat/sys/fs/pstore/console-ramoops>last_kmsg.txt# 方法3:MTK 平台adb shellcat/data/aee_exp/*/db.fatal.*.txt

8.2 内核栈回溯还原

# 1. 找到 vmlinux(未压缩内核镜像)# out/target/product/<device>/obj/KERNEL_OBJ/vmlinux# 2. 还原函数名arm-eabi-addr2line-evmlinux-f-C<PC地址># 3. 反汇编确认arm-eabi-objdump-dvmlinux|grep-A20<函数名>

8.3 常见内核 panic 类型

类型日志特征定位方法
空指针解引用NULL pointer dereference at virtual address 00000000addr2line 还原 PC 地址
内核 BUGkernel BUG at drivers/xxx/yyy.c:123!直接定位到源码行
OOM PanicOut of memory and no killable processes检查内存使用趋势
文件系统错误VFS: Unable to mount root fs检查 eMMC/UFS、分区表

8.4 常见问题排查清单

症状优先检查
插拔充电器重启充电驱动、电源管理(drivers/power/)
特定 App 操作后重启该操作触发的内核路径(GPU、Camera 驱动)
低电量重启电池电量检测、电压保护
高负载压力测试重启散热/Thermal、DVFS 调频
随机无规律重启内存问题(DDR 位翻转)、硬件虚焊
开机过程中重启文件系统挂载、外设初始化

九、总结

  1. Kernel Panic 是内核的"最后防线":关中断 → 打印日志 → 停止其他 CPU → 保存日志到 pstore → 重启系统。

  2. panic() 的执行顺序至关重要:先打印日志再停止其他 CPU,确保日志能输出;kmsg_dump()在smp_send_stop()之后执行,确保日志能写入 pstore。

  3. 内核 Watchdog 的双重保护:Hard lockup 通过 NMI 检测关中断死锁,Soft lockup 通过 hrtimer 检测调度死锁。

  4. pstore/ramoops 是抓住内核崩溃现场的关键:通过保留 DDR 区域让内核日志在重启后依然可读。

  5. 重启原因记录机制:配合 bootreason 属性快速判断是 panic、watchdog 还是用户主动重启。

  6. 定位三板斧:抓last_kmsg→ 找 PC 地址 →addr2line还原代码位置。

下一篇我们将进入 Native 层,深入分析Tombstone 机制——当 native 进程崩溃时,debuggerd 如何生成 tombstone 文件,以及如何从中还原崩溃现场。


本文基于 AOSP 7(Android Nougat, Linux 3.18)源码编写。

相关新闻

  • 从零构建多Agent协作系统:实战拆解AI智能体社交与任务自动化
  • 后端性能优化实战:从JVM调优到系统配置的硬件级提升
  • MHmarkets:从风控思路切入的方法盘点

最新新闻

  • Spring Boot 3 REST API 工程化实践:校验、异常、日志与测试
  • 软件结构图设计:变换分析、事务分析与混合流设计实战指南
  • DNS服务管理:从基础原理到企业级配置实践
  • 靠亏损攒经验的时代过去了:自营考核正全面提速交易成长
  • OpenClaw节点管理:基于WebSocket的物联网设备远程控制与运维实战
  • 计算机毕业设计之基于Spring Boot框架的粮农助手

日新闻

  • 5分钟快速搭建智能数字人:Live2D虚拟形象终极部署指南
  • 告别繁简字幕转换烦恼:这款开源工具让你一键搞定影视字幕处理 [特殊字符]
  • GPT-5.4传闻背后:大模型永久记忆与极限推理的技术演进与挑战

周新闻

  • 怀化母婴除甲醛公司测甲醛中心怎么选:康之居母婴除甲醛标准、流程、避坑指南 - 信誉隆金银铂奢回收
  • 三步打造你的终极音乐中心:foobox-cn网络电台功能完整指南
  • Lance湖仓格式:为多模态AI工作流设计的终极数据存储方案

月新闻

  • ClickHouse版本管理深度实战:4步构建零风险升级与回滚体系
  • Java 23 种设计模式:从踩坑到精通 | 番外:责任链模式 —— 物流审批流程实战
  • 华硕笔记本性能解放指南:G-Helper轻量级控制工具全面解析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号