
1. 为什么内存分析绕不开 vmstat做嵌入式开发的人尤其是碰过 QNX 的大概都有过这种经历系统跑着跑着突然变慢某个进程响应时间从几毫秒飙到几百毫秒日志里什么异常都没有CPU 占用看起来也不高。这时候你打开 top 或者 hogs 看了一眼发现 CPU 确实不忙但系统就是不流畅。问题大概率出在内存上。QNX 作为实时操作系统内存管理机制和 Linux 有本质区别。Linux 有虚拟内存交换分区物理内存不够了可以把不活跃的页换到磁盘上虽然慢但至少不会立刻崩。QNX 不一样它是一个微内核架构的实时系统绝大多数嵌入式场景下根本不配交换分区物理内存就是硬上限。一旦内存分配出现问题轻则进程被内核杀掉重则整个系统进入不可预测的状态。vmstat 这个工具在 QNX 里就是用来观察内存和系统整体运行状态的。它不像 Linux 的 vmstat 那样输出一大堆字段QNX 的 vmstat 输出更精简但每一个字段背后都对应着内核里实实在在的计数器。很多人第一次用 vmstat 的时候会被那一列列数字搞懵不知道哪个该关注、哪个可以忽略、数值到多少算异常。这篇文章就是把我自己在实际项目里反复用 vmstat 排查内存问题的经验整理出来从字段含义到实操判断再到常见坑尽量讲透。不管你是刚接触 QNX 的新手还是已经用过 vmstat 但总觉得没抓到重点的老手下面这些内容应该都能帮你少走一些弯路。我会先讲清楚 vmstat 每个字段到底在说什么然后讲怎么用它定位问题最后分享几个我踩过的坑和排查技巧。2. vmstat 输出字段逐个拆解2.1 先搞清楚 vmstat 的基本用法QNX 下的 vmstat 用法很直接基本格式是vmstat [间隔秒数] [次数]。比如vmstat 1 10就是每秒采样一次一共采十次。如果不带参数直接运行它只输出一行当前值就退出了。实际排查问题的时候我一般用vmstat 2让它一直跑每两秒刷新一行这样既能观察到趋势又不会因为刷新太快导致输出刷屏看不清。有一点需要注意QNX 的 vmstat 在不同版本里输出格式略有差异。QNX 6.5、QNX 7.0、QNX 7.1 的字段名和列顺序可能有细微变化。我下面讲的是以 QNX 7.x 为主如果你用的是更早的版本建议先跑一下use vmstat看看帮助信息确认字段名对得上。vmstat 的输出通常长这样cpu memory page system idle free cache fault active pgin pgout intr csw 85% 120M 45M 1203 12 0 0 450 320不同版本的列可能多一些或少一些但核心字段就这几个。下面我逐个说。2.2 cpu idle别被它骗了cpu idle表示 CPU 空闲百分比。这个值高不代表系统就没问题。我遇到过好几次idle 一直在 80% 以上但系统响应就是慢。原因可能是某个高优先级线程在等内存分配虽然它没占 CPU但整个系统的实时性已经被破坏了。还有一种情况是 idle 看起来正常但intr和csw异常高。这说明 CPU 大量时间花在中断处理和上下文切换上真正干活的时间被碎片化了。这种时候光看 idle 是看不出问题的得结合后面的字段一起判断。我的经验是idle 低于 20% 要警惕 CPU 瓶颈但 idle 高于 80% 同时系统响应慢那问题大概率不在 CPU 上得往内存和中断方向查。2.3 memory free 和 cache此 cache 非彼 cachememory free是当前完全空闲的物理内存量。memory cache是内核用于缓存的内存这部分内存可以被回收再利用。很多人看到 free 很低就紧张其实不一定。如果 cache 很大说明内存被有效利用了系统需要的时候可以快速回收。但 QNX 的 cache 和 Linux 的 page cache 不是一回事。QNX 的 cache 更多是指内核对象缓存、文件系统缓存这些。在嵌入式场景下cache 通常不会特别大因为很多 QNX 系统跑在无文件系统的环境里或者文件系统访问很少。判断内存是否紧张我一般看 free 的绝对值。如果 free 低于总内存的 10%而且 cache 也不大那就要小心了。比如一个 512M 内存的系统free 长期低于 50M这时候任何一次稍大的内存分配都可能失败。还有一个细节QNX 的内存分配是按页来的页大小通常是 4KB。所以 free 的值一定是 4KB 的整数倍。如果你看到 free 不是 4KB 对齐的那可能是显示单位的问题比如显示的是 KB 但实际值经过了取整。2.4 page fault 和 active内存压力的核心指标page fault是页错误次数。在 QNX 里页错误主要发生在两种情况下一是进程访问了尚未映射的虚拟地址内核需要分配物理页并建立映射二是访问了被换出或尚未从文件系统加载的页。由于 QNX 通常没有交换分区第二种情况相对少见主要是第一种。active表示当前活跃的页数也就是最近被访问过的页。这个值高说明内存访问局部性好低说明内存访问分散。但 QNX 的 active 统计方式和 Linux 不同它更多是反映内核认为“最近用过”的页不一定和实际硬件访问完全对应。我一般把 page fault 和 active 结合起来看。如果 page fault 持续很高同时 active 也在增长说明进程在不断申请新内存可能是内存泄漏。如果 page fault 高但 active 稳定可能是进程在频繁映射和解除映射比如反复 mmap/munmap这种操作本身开销就大会拖慢系统。2.5 pgin 和 pgout页面换入换出pgin和pgout是页面换入和换出的次数。在标准 QNX 系统里如果没有配置交换分区这两个值应该一直是 0。如果你看到非零值说明系统配置了某种形式的内存回收机制或者有文件系统页的换入换出。我见过一个案例pgin 和 pgout 持续有值但系统并没有配交换分区。后来查出来是某个进程在反复读取一个大文件文件系统的页缓存不断换入换出。虽然这不是传统意义上的内存交换但同样会消耗 I/O 带宽和 CPU 时间影响实时性。所以看到 pgin/pgout 非零第一件事是确认系统有没有配交换第二件事是查有没有频繁的文件 I/O。2.6 intr 和 csw系统开销的晴雨表intr是中断次数csw是上下文切换次数。这两个值本身不直接反映内存问题但它们能告诉你系统整体有多“忙”。如果 intr 和 csw 都很高说明系统在频繁处理中断和切换任务这时候即使内存充足系统响应也会变慢。我遇到过一次内存问题导致的 csw 飙升。某个进程因为内存分配失败不断重试每次重试都触发一次上下文切换。结果 csw 从正常的几百涨到几千CPU 大量时间花在切换上真正干活的线程反而得不到调度。所以看 vmstat 的时候intr 和 csw 要作为背景参考。如果它们异常高先解决中断和调度问题再回头看内存。3. 用 vmstat 定位内存问题的实操方法3.1 建立基线先知道正常是什么样排查问题的第一步永远是建立基线。系统刚启动、负载正常的时候跑一次vmstat 5 12记录一分钟的数据。重点记下 free、cache、page fault、intr、csw 这几个值的范围。比如一个典型的 QNX 工控板512M 内存启动后 free 大概在 300M 左右cache 在 50M 左右page fault 每秒几十次intr 几百csw 几百。这就是基线。后面如果 free 掉到 100M 以下或者 page fault 涨到几千那就是异常。基线不是跑一次就完事。不同负载场景下基线不同。空载、半载、满载各跑一次心里才有数。我一般会把这些基线值记在项目笔记里下次再遇到类似硬件配置直接拿来参考。3.2 趋势比绝对值更重要单看一行 vmstat 输出很难判断问题。关键是看趋势。比如 free 从 300M 慢慢降到 100M花了几个小时那可能是正常的内存增长。但如果 free 在几分钟内从 300M 掉到 50M那肯定有问题。我习惯用vmstat 2 /tmp/vmstat.log把输出重定向到文件跑一段时间后拿回来分析。QNX 上可以用awk或者写个简单脚本提取关键列画成趋势图。没有图形环境的话直接看数字变化也能看出端倪。有一次我排查一个偶发的系统卡顿vmstat 跑了整整一天。最后发现每次卡顿前page fault 都会有一个尖峰然后 free 骤降。顺着这个线索查下去发现是某个定时任务在特定条件下会申请一大块内存申请失败后不断重试导致系统短暂卡死。3.3 结合其他工具交叉验证vmstat 给的是系统级视图要定位到具体进程还得配合其他工具。QNX 下常用的有pidin、hogs、showmem等。pidin mem可以看每个进程的内存使用情况包括代码段、数据段、堆栈等。如果 vmstat 显示 free 在降用pidin mem找到哪个进程的内存在涨基本就能锁定目标。showmem可以看更详细的内存分配信息包括内核内存池的使用情况。有时候进程内存看起来正常但内核内存池快满了这时候 vmstat 的 free 可能还没明显变化但系统已经开始不稳定了。我一般的排查顺序是vmstat 发现异常 - pidin mem 定位进程 - showmem 确认内核内存状态 - 结合代码分析原因。3.4 一个真实案例的完整排查过程之前有个项目设备运行几天后就会自动重启。日志里没有任何错误信息看门狗直接触发。用 vmstat 观察发现重启前 free 内存会从 200M 快速降到 20M 左右然后系统就挂了。第一步用vmstat 1盯着看确认 free 下降的速度和规律。发现大概每 10 分钟掉 10M 左右很稳定说明是持续泄漏不是突发性的。第二步用pidin mem每隔几分钟抓一次所有进程的内存数据。对比几次数据后发现某个服务进程的堆内存一直在涨其他进程都稳定。第三步查这个服务进程的代码。发现它有个定时任务每次执行都会申请一块内存但异常分支里忘了释放。正常情况下异常分支不会走到但某个特定条件下会触发于是内存就一点点漏掉了。第四步修复后重新跑 vmstatfree 稳定在 180M 左右不再下降问题解决。这个案例里vmstat 的作用是发现问题和确认修复效果真正的定位靠的是 pidin mem 和代码审查。但如果没有 vmstat 第一步的发现后面根本无从下手。4. 常见问题与排查技巧实录4.1 vmstat 输出里哪些值可以忽略不是所有字段都需要关注。比如pgin和pgout在无交换分区的系统里长期为 0你就不用管它。active这个值在 QNX 里参考意义有限因为它的统计口径和实际内存压力关联度不高我一般只看趋势不看绝对值。真正需要重点盯的是free、page fault、intr、csw。这四个值基本能覆盖大部分内存和系统负载问题。4.2 free 内存突然掉很多但找不到进程这种情况通常是内核内存泄漏或者某个驱动在疯狂申请内存。pidin mem看的是进程内存内核内存不在里面。这时候要用showmem看内核内存池。我遇到过一次网卡驱动的问题驱动在处理某种异常包的时候会泄漏内核缓冲区。进程内存完全正常但 free 一直在掉。用 showmem 发现内核内存池的使用量在涨最后定位到驱动。4.3 page fault 高但系统看起来正常page fault 高不一定有问题。如果系统在频繁加载和卸载模块或者进程在反复 mmap/munmappage fault 自然会高。关键看系统响应是否受影响。如果 page fault 高但 idle 也高csw 正常那可能只是某个进程在做正常的内存操作。如果 page fault 高同时 idle 低、csw 高那就是内存操作拖累了系统需要优化。4.4 vmstat 采样间隔怎么选采样间隔太短输出刷屏而且单次采样的瞬时波动可能误导判断。采样间隔太长可能错过短时间的异常。我的经验是初步观察用 5 秒间隔跑几分钟看趋势。发现异常后缩短到 1 秒抓细节。如果要长时间监控用 10 秒或 30 秒间隔减少对系统的影响。注意vmstat 本身也会消耗少量 CPU 和内存在资源极度紧张的系统中高频采样可能加重问题。排查问题时可以适当采样但不要长期开着高频 vmstat。4.5 常见问题速查表现象可能原因排查方向free 持续下降内存泄漏pidin mem 查进程showmem 查内核page fault 尖峰后系统卡顿大块内存申请失败重试查定时任务和异常分支csw 异常高调度频繁可能内存分配失败导致重试结合 intr 和进程状态分析pgin/pgout 非零文件系统页换入换出查文件 I/O 频率free 低但 cache 高正常缓存行为观察 cache 能否回收系统重启前 free 骤降内存耗尽触发看门狗抓重启前的 vmstat 日志4.6 几个我踩过的坑第一个坑只看 free 不看 cache。有次 free 很低我以为是泄漏查了半天没找到。后来发现 cache 很大手动触发回收后 free 就回来了。QNX 的 cache 回收机制和 Linux 不同不能想当然。第二个坑忽略 intr 和 csw。有次内存问题其实不严重但 csw 高得离谱系统响应很差。我先去查内存浪费了很多时间。后来发现是某个中断处理程序写得不好导致大量上下文切换。先解决中断问题内存问题反而变得可接受了。第三个坑在错误的时间点采样。有次系统卡顿是偶发的我跑 vmstat 的时候正好不卡什么异常都没看到。后来改成长时间后台记录才抓到卡顿瞬间的数据。第四个坑忘了看内核内存。进程内存正常不代表系统内存正常。内核内存泄漏更隐蔽危害也更大。5. 把 vmstat 用成条件反射vmstat 这个工具说简单也简单就几个字段。但要用好得把它变成条件反射。系统一有异常第一反应就是跑 vmstat 看看。看得多了自然就能从数字的细微变化里读出问题。我现在的习惯是每个新项目上线前先跑一遍基线记下来。运行过程中定期抽查 vmstat和基线对比。一旦发现偏离立刻深入排查。这样大部分内存问题都能在造成严重后果之前被发现。还有一点vmstat 的输出要结合代码和系统配置一起看。同样的数字在不同系统上含义可能不同。比如一个内存充裕的系统free 低一点没关系但一个内存紧张的系统free 低一点就可能致命。脱离上下文看 vmstat很容易误判。最后分享一个小技巧如果系统支持可以把 vmstat 的输出和进程内存数据一起记录到日志里出问题的时候直接翻日志比事后复现要高效得多。我在几个项目里都这么干省了很多排查时间。