news 2026/10/11 12:27:29

Linux内核内存排查利器:/proc/vmallocinfo详解与实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux内核内存排查利器:/proc/vmallocinfo详解与实战

1. 为什么你需要关注 /proc/vmallocinfo

第一次在服务器上看到/proc/vmallocinfo这个文件的人,大概率是在排查内存泄漏或者内核模块异常的时候。free命令显示内存还够用,slabtop看着也正常,但系统就是越来越卡,这时候有经验的老手会甩给你一句:“去看看 vmallocinfo 吧。”然后你cat一下这个文件,屏幕上刷出几百行十六进制地址和一堆看不懂的函数名,瞬间懵了。

这个文件到底是干什么的?简单说,它是 Linux 内核暴露出来的一个“账本”,专门记录内核虚拟地址空间中通过vmalloc系列函数分配的每一块内存。和kmalloc那种物理地址连续的小块分配不同,vmalloc分配的是虚拟地址连续、物理地址可以不连续的内存区域,通常用于分配较大的缓冲区,比如内核模块加载、驱动初始化、一些子系统的池化内存等。/proc/vmallocinfo就是让你能逐条查看这些分配记录的入口。

它解决的核心问题是:内核虚拟内存分配的可见性。没有它,你只能看到/proc/meminfo里一个笼统的VmallocTotal和VmallocUsed,根本不知道是谁在用、用了多少、有没有泄漏。对于内核开发者、驱动工程师、系统运维人员来说,这个文件是排查内存问题的必备工具。哪怕你只是个刚接触 Linux 的运维新手,学会看这个文件也能让你在遇到“内存够但系统卡”的诡异问题时多一条排查路径。

我写这篇东西的出发点很简单:网上关于/proc/vmallocinfo的资料要么太零散,要么直接甩内核源码,对实际排查问题帮助有限。我把自己这些年在实际工作中反复翻这个文件的经历整理出来,从字段含义到排查思路,再到具体案例,尽量说透。适合已经会用基本 Linux 命令、想深入理解内核内存行为的读者,也适合正在被内存泄漏折磨的同行。

2. 先搞懂 vmalloc 和 kmalloc 的本质区别

2.1 物理连续与虚拟连续的分野

要理解/proc/vmallocinfo,必须先搞清楚vmalloc和kmalloc的根本差异。这两个函数都是内核里申请内存的接口,但底层机制完全不同。

kmalloc分配的内存保证物理地址连续。物理连续意味着 CPU 可以直接通过物理地址访问,不需要额外的页表映射转换,所以访问速度快,适合 DMA 操作和大多数内核数据结构。但物理连续内存是稀缺资源,随着系统运行时间增长,物理内存碎片化越来越严重,想找到一大块连续的物理页越来越难。所以kmalloc通常只用于分配较小的内存块,一般不超过几 MB。

vmalloc则只保证虚拟地址连续,物理地址可以分散在各个不连续的物理页上。内核通过页表把这些分散的物理页映射到一段连续的虚拟地址空间。这样做的好处是:即使物理内存碎片化严重,只要还有足够的空闲页,就能分配出大块虚拟连续内存。代价是每次访问都需要经过页表转换,性能略低,而且不能用于 DMA(因为 DMA 需要物理地址连续)。

打个比方:kmalloc就像在停车场找连续的车位,车少的时候容易找,车多了就难;vmalloc就像给你一个连续的车位编号,但实际停车的位置可以分散在停车场各处,只要编号连续就行。

2.2 什么时候内核会用 vmalloc

内核里用vmalloc的场景其实比很多人想象的要多。常见的有:

  • 内核模块加载:模块的代码段、数据段在加载时通过module_alloc分配,底层就是vmalloc区域。
  • 驱动的大缓冲区:比如某些网卡驱动、存储驱动需要分配几 MB 的环形缓冲区,用vmalloc更稳妥。
  • 内核栈:在配置了CONFIG_VMAP_STACK的系统中,每个线程的内核栈都是通过vmalloc分配的,这是现代内核的默认行为。
  • ioremap 映射:把设备物理地址映射到内核虚拟地址空间时,底层也会用到vmalloc区域。
  • 一些子系统的池化分配:比如percpu分配器、bpf的 JIT 代码区等。

正因为使用场景多,/proc/vmallocinfo里的条目可能非常杂。你可能会看到module_alloc、vmap、ioremap、bpf_jit_alloc_exec等各种调用者的名字。理解这些名字背后的含义,是读懂这个文件的第一步。

2.3 vmalloc 地址空间的布局

在 64 位系统上,内核虚拟地址空间非常宽裕,vmalloc区域通常位于内核地址空间的一个专门区间。以 x86_64 为例,vmalloc区域起始于0xffffc90000000000附近(具体值取决于内核配置和版本),大小可以达到几十 TB。每个vmalloc分配都会在这个区间里找一段空闲的虚拟地址范围,然后建立页表映射。

这个区域是有限的,虽然看起来很大,但如果持续泄漏,最终也会耗尽。一旦vmalloc空间耗尽,内核会报vmalloc: allocation failure之类的错误,严重时导致系统崩溃。所以监控/proc/vmallocinfo不只是看谁在用内存,更是预防虚拟地址空间耗尽的重要手段。

3. /proc/vmallocinfo 文件格式逐字段拆解

3.1 一行记录长什么样

先看一个典型的输出行:

0xffffc90000a00000-0xffffc90000a1c000 114688 module_alloc+0x5d/0x1a0 pages=28 vmap

这一行包含了好几个字段,每个都有明确含义。我逐个拆开讲。

3.2 地址范围:起始和结束

0xffffc90000a00000-0xffffc90000a1c000是这块虚拟内存的起始地址和结束地址。两个地址相减就是这块区域的虚拟大小。比如这里0xffffc90000a1c000 - 0xffffc90000a00000 = 0x1c000,换算成十进制是 114688 字节,也就是 112 KB。

这个地址范围是虚拟地址,不是物理地址。你没法直接用它去对应物理内存,但可以通过页表反查。实际排查中,这个地址范围主要用来判断分配是否对齐、是否有异常大的块。

3.3 大小字段:字节数

紧接着地址范围后面的数字就是这块分配的大小,单位是字节。上面例子里的114688就是 112 KB。这个值通常和地址范围算出来的大小一致,但有时候会因为对齐原因略有差异。

看这个字段的时候要有“量级感”:几 KB 到几十 KB 通常是正常的小分配;几 MB 到几十 MB 就要留意是谁在用;如果看到几百 MB 甚至 GB 级别的单条记录,那基本可以确定有问题。

3.4 调用者信息:谁申请的

module_alloc+0x5d/0x1a0这一串是调用者信息。module_alloc是函数名,+0x5d/0x1a0表示在module_alloc函数偏移0x5d处调用了分配函数,该函数总长度0x1a0。这个信息来自内核的符号解析,能帮你快速定位是哪个子系统申请的。

常见的调用者名字包括:

调用者含义
module_alloc内核模块加载
vmap通用 vmalloc 映射
ioremap设备物理地址映射
bpf_jit_alloc_execBPF JIT 编译代码区
pcpu_allocper-CPU 分配器
alloc_vm_area通用虚拟区域分配
__get_vm_area_nodevmalloc 底层分配函数

看到不认识的函数名,可以用grep在内核源码里搜,或者用addr2line工具反查。

3.5 pages 字段:物理页数量

pages=28表示这块虚拟内存背后实际映射了 28 个物理页。在 4 KB 页大小的系统上,28 页就是 112 KB,和前面的字节数对得上。但在某些情况下,pages可能小于虚拟大小对应的页数,比如ioremap映射设备内存时,物理页可能不是常规 RAM 页。

这个字段的价值在于:它告诉你实际占用了多少物理内存。虚拟大小可能因为对齐而偏大,但pages反映的是真实物理页占用。

3.6 标志位:vmap、user、ioremap 等

行尾可能出现的标志包括:

  • vmap:表示这是一块通过vmap建立的映射,物理页已经存在,只是建立虚拟映射。
  • user:表示这块区域映射到了用户空间,通常和ioremap或特殊驱动有关。
  • ioremap:表示这是设备内存映射,不是常规 RAM。
  • nopage:表示没有关联物理页,可能是预留区域。

这些标志能帮你区分分配类型。比如ioremap的区域不计入常规内存统计,排查内存泄漏时可以排除。

4. 实操:如何高效读取和分析 vmallocinfo

4.1 基础读取命令

最直接的读取方式就是cat:

cat /proc/vmallocinfo

但输出可能很长,几百上千行很常见。更好的方式是配合less或重定向到文件:

cat /proc/vmallocinfo > /tmp/vmallocinfo.txt wc -l /tmp/vmallocinfo.txt

先看总行数,心里有个底。如果超过几千行,说明系统里 vmalloc 分配非常频繁,需要重点关注。

4.2 按大小排序找大块分配

排查内存问题时,第一步通常是找大块分配:

awk '{print $2, $0}' /proc/vmallocinfo | sort -rn | head -20

这个命令把每行的第二个字段(大小)提出来,按数字降序排列,取前 20 条。这样你能一眼看到最大的分配是谁。

我实际用的时候还会加个格式化,把字节数转成人类可读的:

awk '{printf "%.2f MB %s\n", $2/1024/1024, $0}' /proc/vmallocinfo | sort -rn | head -20

4.3 按调用者聚合统计

想知道哪个子系统占用最多,可以按调用者名字聚合:

awk '{print $3}' /proc/vmallocinfo | sed 's/+.*//' | sort | uniq -c | sort -rn | head -20

这个命令提取第三字段,去掉偏移部分,统计每个调用者出现的次数。出现次数多不一定占用内存多,但能反映分配频率。

如果要统计每个调用者的总字节数:

awk '{sum[$3]+=$2} END {for (k in sum) printf "%10.2f MB %s\n", sum[k]/1024/1024, k}' /proc/vmallocinfo | sort -rn | head -20

这个更有价值,能直接看出谁占的内存最多。

4.4 监控变化:两次采样对比

单次快照只能看当前状态,要判断有没有泄漏,需要间隔一段时间采样两次,对比差异:

cat /proc/vmallocinfo > /tmp/vm1.txt sleep 60 cat /proc/vmallocinfo > /tmp/vm2.txt diff /tmp/vm1.txt /tmp/vm2.txt | head -50

如果两次之间新增了大量条目,或者某些条目大小持续增长,那就说明有泄漏嫌疑。我一般会间隔 5 到 10 分钟采样,观察趋势。

注意:/proc/vmallocinfo的读取本身会短暂持有内核锁,在高负载系统上频繁读取可能造成轻微性能抖动。生产环境排查时尽量避开业务高峰,或者用taskset绑定到空闲 CPU 上执行。

5. 典型排查案例:从 vmallocinfo 定位内存泄漏

5.1 案例背景:系统内存缓慢增长

某次在一台运行了较长时间的服务上,监控显示VmallocUsed持续增长,从最初的几百 MB 涨到了几个 GB。系统没有明显报错,但dmesg里偶尔出现vmalloc: allocation failure的警告。free显示物理内存还有余量,但 vmalloc 空间已经紧张。

5.2 第一步:抓取快照并排序

先抓一份当前快照:

cat /proc/vmallocinfo > /tmp/vm_before.txt awk '{printf "%.2f MB %s\n", $2/1024/1024, $0}' /tmp/vm_before.txt | sort -rn | head -30

发现前几名里有大量bpf_jit_alloc_exec的条目,单个不大(几十 KB),但数量极多,加起来占了将近 1 GB。这很不正常,因为系统上并没有运行大量 BPF 程序。

5.3 第二步:按调用者聚合确认

awk '{sum[$3]+=$2} END {for (k in sum) printf "%10.2f MB %s\n", sum[k]/1024/1024, k}' /tmp/vm_before.txt | sort -rn | head -10

输出确认bpf_jit_alloc_exec总占用排第一,远超其他调用者。问题定位到 BPF JIT 代码区。

5.4 第三步:分析原因

BPF JIT 代码区用于存放编译后的 BPF 程序机器码。正常情况下,BPF 程序加载后会占用一块,卸载后释放。但如果程序频繁加载卸载,而释放路径有问题,就会导致代码区只增不减。

进一步检查发现,某个监控代理在每次采集数据时都会动态加载一个 BPF 程序,采集完就卸载。但由于内核版本的一个已知问题,卸载时 JIT 代码区没有完全释放,导致每次采集都泄漏几十 KB。运行几天后累积到 GB 级别。

5.5 第四步:验证和解决

为了验证,我写了个小脚本,每隔 30 秒记录一次bpf_jit_alloc_exec的总大小:

while true; do total=$(awk '/bpf_jit_alloc_exec/ {sum+=$2} END {print sum}' /proc/vmallocinfo) echo "$(date +%s) $total" sleep 30 done

观察几个小时,确认总大小呈线性增长。升级内核到修复版本后,问题消失。

这个案例说明,/proc/vmallocinfo不仅能告诉你“谁在用内存”,还能通过聚合和趋势分析定位到具体的泄漏点。如果没有这个文件,面对VmallocUsed的增长,你只能盲猜。

6. 常见问题与排查技巧速查

6.1 为什么 vmallocinfo 里的总大小和 VmallocUsed 对不上

/proc/meminfo里的VmallocUsed统计的是所有 vmalloc 分配的总和,但它的计算方式在不同内核版本里有差异。有些版本只统计vmap区域,有些包含ioremap,还有些会把已释放但未回收的区域算进去。而/proc/vmallocinfo是逐条列出当前活跃的分配,理论上更准确。

如果你发现两者差异很大,先确认内核版本,然后检查是否有大量ioremap区域(这些通常不计入VmallocUsed)。另外,/proc/vmallocinfo的读取本身可能因为并发分配而略有偏差,但不会差太多。

6.2 条目太多看不过来怎么办

这是最常见的问题。我的经验是分三步走:

  1. 先看总量:wc -l看行数,awk求和看总大小。
  2. 按调用者聚合:用前面给的awk命令,找出占用最大的几个调用者。
  3. 针对性细看:只关注那几个调用者的条目,看地址范围、大小、标志位是否正常。

不要试图逐行读完,那样效率太低。

6.3 如何判断某个分配是否正常

判断标准因调用者而异。比如:

  • module_alloc:每个已加载模块对应几条,大小和模块大小相关,通常几十 KB 到几 MB。
  • bpf_jit_alloc_exec:每个活跃 BPF 程序对应一条,数量应该和 BPF 程序数匹配。
  • pcpu_alloc:per-CPU 分配,数量应该和 CPU 核心数相关,不会太多。
  • ioremap:设备映射,数量应该和驱动初始化次数匹配,不会持续增长。

如果某个调用者的条目数量远超预期,或者总大小持续增长,就是异常信号。

6.4 读取 vmallocinfo 会影响性能吗

会有一点。读取这个文件需要遍历内核的 vmalloc 区域链表,并持有vmap_area_lock自旋锁。在分配频繁的系统上,这个锁竞争可能比较激烈,读取操作会短暂阻塞其他分配。所以:

  • 不要在高频循环里反复读取。
  • 生产环境排查时,尽量间隔几分钟采样一次。
  • 如果系统已经卡顿,读取这个文件可能让情况更糟,要谨慎。

6.5 有没有替代工具

有。/proc/vmallocinfo是最底层的接口,但有些工具封装了它:

  • vmallocinfo脚本:一些发行版自带的分析脚本,能自动聚合排序。
  • crash工具:在分析内核转储时,可以用vmalloc命令查看类似信息。
  • drgn:更现代的内核调试工具,可以编程方式访问 vmalloc 区域。

但无论用什么工具,底层数据都来自/proc/vmallocinfo,理解它的格式是基础。

7. 进阶:从 vmallocinfo 反推内核行为

7.1 通过地址范围判断分配顺序

/proc/vmallocinfo里的条目通常按地址升序排列。地址越低,分配越早。如果你看到某个调用者的条目地址普遍偏高,说明是最近才分配的。这个特性可以用来判断泄漏发生的时间窗口。

比如你发现bpf_jit_alloc_exec的条目地址从某个值开始密集出现,而那个地址对应的时间点正好是某个服务启动之后,那基本可以锁定是那个服务导致的。

7.2 结合其他 proc 文件交叉验证

单独看 vmallocinfo 有时不够,需要结合其他文件:

  • /proc/meminfo:看VmallocTotal、VmallocUsed、VmallocChunk的整体趋势。
  • /proc/slabinfo:排除 slab 分配器的干扰。
  • /proc/modules:确认已加载模块列表,和module_alloc条目对应。
  • /sys/kernel/debug/tracing:用 ftrace 跟踪vmalloc和vfree调用,看是否有不配对的分配释放。

我通常先用 vmallocinfo 定位到可疑调用者,再用 ftrace 跟踪具体调用栈,这样能精确到代码行。

7.3 内核配置对 vmallocinfo 的影响

不是所有内核都完整支持/proc/vmallocinfo。它依赖于CONFIG_PROC_VMCORE或CONFIG_MMU等配置。在一些嵌入式系统或精简内核上,这个文件可能不存在或内容不全。

另外,CONFIG_VMAP_STACK开启后,每个线程的内核栈都会出现在 vmallocinfo 里,条目数量会大幅增加。这时候要区分“正常的大量条目”和“异常泄漏”,不能只看数量。

提示:如果你在容器环境里看不到/proc/vmallocinfo,可能是因为容器挂载了独立的 proc 文件系统,或者内核配置裁剪了该功能。需要在宿主机上查看。

8. 我个人的实操体会

这些年翻/proc/vmallocinfo的次数不少,有几个体会比较深。

第一,不要孤立地看这个文件。它只是内核内存视图的一个切面,必须结合meminfo、slabinfo、dmesg一起看。单独看 vmallocinfo 很容易误判,比如把正常的模块加载当成泄漏。

第二,聚合分析比逐行看有效得多。几百行输出里,真正有问题的可能就那几条。用awk聚合能快速缩小范围,把精力集中在可疑调用者上。

第三,趋势比快照重要。单次快照只能看当前状态,间隔采样对比才能发现泄漏。我一般会写个简单脚本,每隔几分钟记录一次关键调用者的总大小,画成趋势图,一眼就能看出问题。

第四,内核版本差异要留意。不同内核版本里,vmallocinfo 的字段格式、调用者名字、统计方式都可能有变化。排查前先确认内核版本,查对应的文档或源码,避免用旧经验套新系统。

最后分享一个小技巧:如果你怀疑某个模块泄漏,可以先用rmmod卸载它,再看 vmallocinfo 里对应的module_alloc条目是否消失。如果没消失,说明释放路径有问题。这个操作在测试环境里很有效,生产环境要谨慎,因为卸载模块可能影响业务。

这个文件后续还可以这样扩展:结合perf或eBPF做实时监控,当 vmalloc 分配超过阈值时自动告警;或者写个解析脚本,把 vmallocinfo 输出转成火焰图,直观展示各调用者的内存占用比例。这些我都试过,效果不错,有机会再单独整理。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/11 12:26:31

YOLOv9表情识别系统:人脸检测+对齐+七类分类全流程实现

简介:本资源是一个基于YOLOv9的端到端面部表情识别系统实现,面向计算机视觉初学者、机器学习实践者及毕业设计开发者,解决实时人脸表情检测与分类问题,适用于人机交互、心理学实验辅助、情绪分析等场景。压缩包共107个文件&#x…

作者头像 李华
网站建设 2026/10/11 12:26:28

SLAM源码修改版全解析:从编译到精度评估

简介:面向深蓝学院教学与科研需求定制的高博《自动驾驶与机器人中的SLAM技术》源码修改版,将书中理论与可运行的C/C代码实现逐一对应,适合正在学习视觉里程计、后端优化、回环检测、建图与定位的自动驾驶和机器人方向读者,也适合希…

作者头像 李华
网站建设 2026/10/11 12:23:42

基于AI+Spring Boot+微信小程序的数字博物馆系统毕设实战指南

带毕业设计这件事,很多同学一上来就问“这个系统怎么做”,但真正劝退人的往往不是代码,而是前期需求压根没想清楚。拿“基于AISpring Boot微信小程序的数字博物馆系统”这个题目来说,它把当下最热的两条线都占了:一条是…

作者头像 李华
网站建设 2026/10/11 12:23:11

自助图文打印系统全解析:小程序+PHP后端实现扫码打印闭环

简介:这套全新UI自助图文打印系统小程序源码,以PHP后端为支撑,适合图文快印店主、独立开发者及需要快速上线自助打印服务的技术团队。资源包含完整的前后端工程,后端采用ThinkPHP框架,前端为微信小程序,并附…

作者头像 李华