3、基础调试工具(下):内存、IO与内核日志

上一节我们把进程级调试工具过了一遍。这一节接着聊剩下的几个——它们分别盯着内存、磁盘 IO 和内核日志。说实话,这三个方向在嵌入式 Linux 调试里出现的频率非常高。我自己的经验是,系统卡顿、响应慢、莫名其妙重启,十有八九都能从这几个工具里找到线索。

3.1 内存与进程监控:top 和 ps 的进阶用法

很多人用 top 就是看一眼 CPU 占用率。其实在 Tina Linux 这种资源受限的系统里,内存才是真正的瓶颈。我个人习惯先按 M 键让进程按内存占用排序,看看谁在吃内存。

关键指标解读:

  • VIRT:虚拟内存大小。这个值通常很大,但不代表实际物理内存占用。
  • RES:常驻物理内存。这才是真正占用的内存,重点关注。
  • SHR:共享内存。多个进程可能共用,计算总内存时别重复算。

我曾经遇到一个案例:摄像头预览画面越来越卡,用 top 一看,某个 ISP 驱动进程的 RES 从 8MB 涨到了 60MB,而且还在涨。这就是典型的内存泄漏。嗯,这里要注意,嵌入式设备没有 swap,内存泄漏就是死罪。

ps 的用法更灵活。我常用的组合是:

# 查看所有进程的完整命令行
ps -ef

# 按内存占用排序(降序)
ps aux --sort=-%mem

# 查看某个线程的详细信息
ps -T -p <PID>

小技巧:在 Tina Linux 里,ps 默认是 BusyBox 版本,参数和标准 Linux 略有不同。如果 ps aux 不好使,试试 ps -o pid,ppid,%mem,%cpu,cmd 自定义输出。

3.2 内存全景图:/proc/meminfo 解读

/proc/meminfo 是内核暴露内存信息的窗口。我每次定位内存问题,第一件事就是 cat /proc/meminfo。你想想看,系统总共 128MB 内存,到底被谁吃了?这里一目了然。

字段 含义 排查要点
MemTotal 总物理内存 和硬件规格核对,确认 BIOS/DT 有没有认错
MemFree 完全空闲的内存 如果长期低于 5%,系统可能处于内存紧张状态
Cached 文件缓存(可回收) 这部分内存被内核用来加速文件访问,压力下可释放
Buffers 块设备缓存 和 Cached 类似,但针对原始块设备
Slab 内核对象缓存 如果异常增大,可能是内核模块或驱动有泄漏
MemAvailable 估算可用内存 比 MemFree 更准确,包含了可回收的缓存

举个例子。有一次客户反馈设备运行 3 天后变得极慢。我远程上去一看,MemFree 只剩 2MB,但 Cached 高达 80MB。这说明内核缓存了大量文件数据,但应用层其实没怎么用。我手动执行 echo 3 > /proc/sys/vm/drop_caches 释放缓存,系统立刻恢复流畅。当然,这只是临时手段,根本原因还得查为什么缓存一直不释放。

注意:在 Tina Linux 中,/proc/meminfo 的某些字段(如 MemAvailable)在较老的内核版本中可能不存在。如果看不到,就用 MemFree + Cached + Buffers 粗略估算可用内存。

3.3 IO 与性能分析:iostat 和 vmstat

IO 性能问题在嵌入式设备上很隐蔽。你想想看,CPU 占用率不高,但系统就是慢。这时候就该 iostat 出场了。

# 每隔 2 秒输出一次 IO 统计,共输出 5 次
iostat -x 2 5

重点关注这几个指标:

  • %util:磁盘忙闲比例。接近 100% 说明磁盘是瓶颈。
  • await:IO 请求平均等待时间(毫秒)。超过几十毫秒就不正常了。
  • svctm:IO 服务时间。如果远小于 await,说明请求在排队。

我记得有一次调试 TF 卡读写慢的问题。iostat -x 显示 %util 只有 30%,但 await 高达 200ms。这说明卡本身不忙,但驱动层或文件系统层有延迟。后来发现是文件系统挂载参数没加 noatime,每次读文件都要写时间戳,IO 路径被拉长了。

vmstat 是个更全面的工具。它一次输出 CPU、内存、IO 三方面的信息。

# 每隔 1 秒输出一次
vmstat 1
类别 字段 说明
procs r / b r 是运行队列,b 是阻塞队列。b 持续大于 0 说明 IO 有问题
memory swpd / free / buff / cache 嵌入式无 swap,swpd 通常为 0;cache 过大可回收
swap si / so 换入换出。Tina Linux 一般没 swap,看到非零值要警惕
io bi / bo 块设备每秒读写(块)。突增说明有大量磁盘操作
system in / cs 中断数和上下文切换数。过高说明系统在空转
cpu us / sy / id / wa / st wa 是 IO 等待 CPU 时间。wa 高 = IO 瓶颈

我个人的排查流程是这样的:先看 vmstat 1 的 wa 列,如果持续超过 30%,基本可以断定 IO 是瓶颈。然后切到 iostat -x 定位具体是哪块盘、哪个分区。最后用 strace 或 lsof 找到是哪个进程在疯狂读写。

3.4 内核日志分析:dmesg 的实战用法

dmesg 是内核的“日记本”。驱动加载、硬件错误、内存不足、文件系统异常……内核都会往这里写。我每次拿到一台新设备,第一件事就是 dmesg | grep -i error,看看有没有隐藏的坑。

# 查看所有内核日志
dmesg

# 只看错误和警告
dmesg -l err,warn

# 实时监控内核日志(类似 tail -f)
dmesg -w

# 清空日志(调试前先清空,方便后续定位)
dmesg -c

实战中我遇到过好几次这样的情况:设备启动后 WiFi 连不上,应用层查了半天没结果。最后 dmesg 一看,有一条 mmc0: error -110 whilst initialising SDIO card。这说明 SDIO 接口的 WiFi 模块初始化失败了,根本不是应用层的问题。

常见 dmesg 错误模式:

  • Out of memory 或 Killed process:内存不足,内核杀了进程。
  • Buffer I/O error:存储介质有坏块或连接不稳定。
  • watchdog: BUG: soft lockup:某个 CPU 核卡住了,通常是驱动死循环。
  • irq timeout:中断响应超时,硬件或驱动有问题。

另外,dmesg 的日志是存在内核环形缓冲区里的,大小有限。如果系统运行时间很长,早期的日志会被覆盖。我建议在调试阶段,把关键日志通过 syslogd 或 logread 保存到文件里。

避坑指南:我曾经在调试时反复 dmesg -c 清空日志,结果后来出问题时找不到历史记录。现在我的习惯是:清空前先 dmesg > /tmp/dmesg_before.log 备份一份。

3.5 知识体系总览

下面这张图把本章的工具和它们各自盯的子系统串起来了。你可以把它当作排查时的路线图。

基础调试工具知识体系 基础调试工具知识体系 嵌入式 Linux 系统资源 内存子系统 IO 子系统 内核日志 top / ps / meminfo iostat / vmstat dmesg 关键指标 MemFree / Cached / Slab VIRT / RES / SHR 关键指标 %util / await / svctm bi/bo / wa / r/b 关键模式 OOM / IO error / soft lockup irq timeout / init failure 典型问题:内存泄漏 / IO 瓶颈 / 驱动异常 / 系统卡顿 排查路线:dmesg → vmstat → iostat → top/ps → meminfo

这张图把三个工具链和它们对应的子系统、关键指标、典型问题串在了一起。实际排查时,我通常从 dmesg 开始扫一遍,排除明显的硬件或驱动问题。然后用 vmstat 看整体负载,如果 wa 高就切 iostat,如果内存低就切 top 和 meminfo。这个顺序能帮你快速缩小范围,而不是漫无目的地瞎试。