4、内核日志分析:dmesg命令详解、内核日志级别、常见内核错误解读
好,咱们进入内核日志的世界。说实话,做Android系统层开发,如果看不懂dmesg,那就像医生看不懂X光片。很多疑难杂症,比如系统突然重启、死机、外设失灵,根因往往就藏在内核日志里。今天我就带你把它彻底搞明白。
4.1 dmesg命令详解:不只是敲个命令那么简单
dmesg 这个命令,说白了就是用来读取内核环形缓冲区(kernel ring buffer)的。内核在运行过程中,会不断往这个缓冲区里写消息,比如驱动加载、硬件中断、内存分配失败等等。我们用 dmesg 就能把这些消息倒出来看。
基本用法很简单,但有几个参数我建议你记牢:
# 查看所有内核日志
dmesg
# 实时监控内核日志(类似 logcat -v time)
dmesg -w
# 只看最后20条
dmesg | tail -20
# 按人类可读的时间戳显示
dmesg -T
# 清除内核环形缓冲区(谨慎使用!)
dmesg -c
我个人习惯,在抓取问题现场时,一定会先执行 dmesg -T > /data/dmesg_$(date +%Y%m%d_%H%M%S).log。为什么?因为 -T 参数能把内核启动以来的秒数转换成可读的时间,这样你才能和logcat里的时间对上号。
dmesg,因为内核环形缓冲区在重启后不会被清空(除非断电)。上次崩溃前的日志往往还在里面。
4.2 内核日志级别:一眼看出消息的紧急程度
内核日志有8个级别,从0到7,数字越小越紧急。我刚开始接触时也记不住,后来总结了个口诀:「越小的数字,越要命的错误」。
| 级别 | 名称 | 含义 | 常见场景 |
|---|---|---|---|
| 0 | KERN_EMERG | 紧急事件,系统不可用 | 内核panic、硬件故障 |
| 1 | KERN_ALERT | 必须立即处理 | 严重内存错误 |
| 2 | KERN_CRIT | 临界条件 | 驱动崩溃、IO错误 |
| 3 | KERN_ERR | 错误条件 | 设备初始化失败 |
| 4 | KERN_WARNING | 警告 | 资源不足、降级运行 |
| 5 | KERN_NOTICE | 正常但重要 | 挂载文件系统 |
| 6 | KERN_INFO | 信息性消息 | 驱动版本、硬件信息 |
| 7 | KERN_DEBUG | 调试信息 | 函数调用跟踪 |
你在dmesg输出里会看到类似 <3> 这样的前缀,那就是日志级别。比如 <3>[ 12.345] mmc0: error -110,说明这是一个KERN_ERR级别的错误。
dmesg | grep -E "<0>|<1>|<2>|<3>" 过滤出级别0-3的日志。这些才是真正需要关注的「硬伤」。
4.3 常见内核错误:panic、oops、watchdog
这三个词,做系统层的工程师听到都会心头一紧。我来逐个拆解。
4.3.1 Kernel Panic:最严重的系统崩溃
Kernel Panic,说白了就是内核遇到了无法恢复的错误,只能选择「自杀」——直接挂起整个系统。你会在屏幕上看到一堆调用栈,最后一行通常是 Kernel panic - not syncing: ...。
我在项目中遇到过最典型的一次:某个低端设备在播放视频时频繁panic。最后定位到是GPU驱动在特定分辨率下访问了非法内存地址。嗯,这里要注意,panic日志里的PC指针(Program Counter)和LR寄存器(Link Register)是最关键的线索。
[ 123.456] Kernel panic - not syncing: Fatal exception
[ 123.457] CPU: 2 PID: 1234 Comm: mediaserver Tainted: P
[ 123.458] Hardware name: Qualcomm Technologies, Inc. MSMxxxx
[ 123.459] Call trace:
[ 123.460] [<ffffff800808c000>] dump_backtrace+0x0/0x1a0
[ 123.461] [<ffffff800808c1a0>] show_stack+0x20/0x30
[ 123.462] [<ffffff80086a5a10>] __schedule_bug+0x60/0x70
__schedule_bug 才是根因。
4.3.2 Kernel Oops:内核的「轻伤」
Oops比Panic轻一些。内核在遇到Oops时,会尝试继续运行,但系统已经处于不稳定状态。你想想看,内核的某个关键数据结构被破坏了,它还在硬撑着,这就像一个人腿断了还在走路——随时可能倒下。
Oops日志里通常会包含寄存器值、堆栈内容、以及出错的指令地址。我建议你重点关注 Unable to handle kernel NULL pointer dereference 或 Unable to handle kernel paging request 这类描述。说白了,就是内核访问了一个不该访问的地址。
[ 234.567] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000010
[ 234.568] Mem abort info:
[ 234.569] ESR = 0x96000046
[ 234.570] EC = 0x25: DABT (current EL), IL = 32 bits
[ 234.571] SET = 0, FnV = 0
[ 234.572] EA = 0, S1PTW = 0
[ 234.573] Data abort info:
[ 234.574] ISV = 0, ISS = 0x00000046
[ 234.575] CM = 0, WnR = 1
这里有个避坑指南:我曾经在分析一个Oops时,看到 NULL pointer dereference 就以为是空指针问题。结果查了半天,发现是某个驱动在释放内存后没有把指针置NULL,导致野指针被再次使用。所以,遇到Oops,不仅要看地址,还要看是谁触发了这次访问。
4.3.3 Watchdog:系统的「看门狗」
Watchdog,直译就是看门狗。它是个硬件或软件定时器,内核需要定期去「喂狗」(重置定时器)。如果超过一定时间没喂狗,看门狗就会认为系统卡死了,然后强制重启。
常见的watchdog日志长这样:
[ 345.678] watchdog: BUG: soft lockup - CPU#0 stuck for 22s!
[ 345.679] Modules linked in: ...
[ 345.680] CPU: 0 PID: 5678 Comm: kworker/u16:2
[ 345.681] Call trace:
[ 345.682] [<ffffff80080a1234>] __do_softirq+0x100/0x200
为什么会发生soft lockup?说白了,就是某个CPU核心在22秒内没有切换任务,一直卡在某个中断处理或内核线程里。我遇到过最离谱的一次,是某个WiFi驱动在特定条件下进入了死循环,导致整个CPU被锁死。嗯,这里要注意,watchdog日志里的 stuck for 22s 这个时间是可以配置的,默认一般是20秒左右。
4.4 Kernel Log与Logcat的关联分析
这是实战中最关键的一环。很多同学只会单独看logcat或单独看dmesg,但真正的系统级问题,往往需要把两者结合起来。
我的做法是这样的:
- 时间对齐:先用
dmesg -T把内核时间转换成系统时间,然后和logcat里的时间戳对比。注意,logcat用的是YYYY-MM-DD HH:MM:SS.mmm格式,而dmesg -T输出的是[Mon Jan 1 12:34:56 2024]格式,需要手动对齐。 - 寻找关联点:比如logcat里看到
E/Netd( 1234): Unable to connect to socket,那你就去dmesg里搜同一时间附近的网络驱动错误。我遇到过很多次,logcat里报的是应用层错误,根因其实是内核里的WiFi驱动挂了。 - 关注关键事件:系统重启前,logcat里通常会有
I/ServiceManager( 567): service 'xxx' died之类的日志,而dmesg里则会有Kernel panic或watchdog的记录。把这两个时间点对上,就能确定是谁先出问题。
E/AndroidRuntime: Process com.android.phone died,看起来像是应用崩溃。但我习惯性地看了dmesg,发现每次重启前都有 watchdog: BUG: soft lockup - CPU#3 stuck for 22s!,而且卡住的位置都在同一个modem驱动里。最后定位到是基带固件的一个bug,在特定信号环境下导致内核死锁。如果只看logcat,这个问题永远查不出来。
最后,我建议你养成一个习惯:每次抓取logcat时,顺手把dmesg也抓下来。这两个日志就像人的「言行」——logcat是说的话(应用层行为),dmesg是身体状态(内核层健康)。只有两者结合,才能做出准确的诊断。