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 知识体系总览
下面这张图把本章的工具和它们各自盯的子系统串起来了。你可以把它当作排查时的路线图。
这张图把三个工具链和它们对应的子系统、关键指标、典型问题串在了一起。实际排查时,我通常从 dmesg 开始扫一遍,排除明显的硬件或驱动问题。然后用 vmstat 看整体负载,如果 wa 高就切 iostat,如果内存低就切 top 和 meminfo。这个顺序能帮你快速缩小范围,而不是漫无目的地瞎试。