背景

内存分配剖析(MAP)的数据经 /proc/allocinfo 暴露给用户态。这个文本接口手工查看够用,放到生产监控与大规模分析里就吃力了。

文本接口的全量开销
┌────────────────────────────────────┐
│ read() 对上千个 tag 逐个聚合全 CPU │
│ 计数器                             │
└─────────────────┬──────────────────┘
                  ▼
┌────────────────────────────────────┐
│        全量文本拷贝到用户态        │
└─────────────────┬──────────────────┘
                  ▼
┌────────────────────────────────────┐
│ 用户态再解析过滤,多数数据白跑一趟 │
└────────────────────────────────────┘

问题

  • 用户态要解析大量文本才能取出要的字段
  • 找特定 tag 得全量读取,上下文切换与数据拷贝多
  • 内核为每个 code tag 聚合全 CPU 的 per-CPU 计数器,哪怕用户马上把它过滤掉

方案

给 /proc/allocinfo 加 IOCTL 二进制接口,把过滤条件下推到聚合之前:内核只对命中条件的少数 tag 做 per-CPU 计数器聚合,不命中的完全不碰。

过滤条件下推到聚合之前
┌──────────────────────────────────┐
│       ioctl 先下推过滤条件       │
└────────────────┬─────────────────┘
                 ▼
┌──────────────────────────────────┐
│ 只对命中 tag 聚合 per-CPU 计数器 │
└────────────────┬─────────────────┘
                 ▼
┌──────────────────────────────────┐
│  二进制记录按需取,文本解析全省  │
└──────────────────────────────────┘

接口提供 3 个命令:ALLOCINFO_IOC_CONTENT_ID 取内容标识,模块加载卸载会让它变化,用户在读取前后各取一次即可校验数据一致性;ALLOCINFO_IOC_GET_AT 按位置取记录;ALLOCINFO_IOC_GET_NEXT 取下一条记录。

过滤维度逐步铺开:按 tag 的模块名、函数名、文件名、行号过滤(这类名字常有公共前缀,比较时取末 64 个字符降低碰撞);按 [min_size, max_size] 尺寸区间过滤,这个区间含端点,且因为要真的取出计数器,比其他过滤慢;按 accuracy 过滤。

收益

作者在 Intel Xeon Platinum 8481C(224 CPU)上测量,每次运行前 drop caches,sys 时间:

场景 传统 read() IOCTL 改善
按文件名过滤 22ms 1ms 约 22 倍
文件名加尺寸复合过滤 21ms 1ms 约 21 倍
按尺寸过滤 21ms 14ms 1.5 倍