4. NUMA感知的网络性能分析工具
在多核服务器上排查网络性能问题,单看 top 或 ifconfig 往往只能看到“CPU 很忙、网卡有流量”这类表层现象,无法回答“包被哪个 NUMA 节点的核处理、内存分配是否跨节点、中断是否集中在一个 socket、队列是否打满”这些关键问题。本节把六类工具按“观察层次”组织起来:numactl 负责绑定与执行控制,numastat 负责节点级内存分布统计,perf 负责 CPU 与内核路径采样,ftrace 负责内核函数级时序追踪,DPDK telemetry 负责用户态轮询模式的运行时指标,网卡统计负责硬件队列与丢包计数。它们不是互相替代,而是从“进程→节点→内核→驱动→硬件”逐层收敛证据。
一、numactl:把进程和内存钉在正确的节点上
numactl 的核心价值是控制执行位置。常用形式有两类:一是查看拓扑,二是带绑定策略运行程序。先看拓扑:
numactl --hardware
# 输出示例(示例环境,非实测):
# available: 2 nodes (0-1)
# node 0 cpus: 0 1 2 3 4 5 6 7
# node 0 size: 32768 MB
# node 1 cpus: 8 9 10 11 12 13 14 15
# node 1 size: 32768 MB
# node distances:
# node 0 1
# 0: 10 21
# 1: 21 10
距离矩阵中 10 表示本地访问,21 表示跨节点访问。若网卡插在 node 0 的 PCIe 插槽,而处理软中断的核在 node 1,就会出现“包在 node 0 收、内存在 node 1 分配”的跨节点路径。此时可用 numactl --cpunodebind=0 --membind=0 ./app 把进程的 CPU 和内存都限制在 node 0。对于多队列网卡,更常见的做法是让每个队列的中断亲和到同节点 CPU,再用 numactl 启动对应 worker。
二、numastat:看内存到底落在哪个节点
numactl 管“应该在哪”,numastat 看“实际在哪”。直接运行 numastat 会给出各节点的 numa_hit、numa_miss、local_node、other_node 等计数。判断要点是:numa_miss 和 other_node 持续增长,说明进程在本地节点分配失败或被迫跨节点分配。例如某进程在 node 0 运行,但 node 0 内存被其他服务占满,内核就会到 node 1 分配,表现为 node 0 的 numa_miss 上升、node 1 的 other_node 上升。此时应检查是否设置了 membind、是否内存超分、是否需要把进程迁到内存充足的节点。
三、perf:定位热点函数与跨节点访存
perf 适合回答“时间花在哪”。对网络性能,常用三类用法:
perf top -g:实时看内核/用户态热点,快速判断是协议栈、驱动还是应用逻辑占 CPU。perf record -g -a -- sleep 10后perf report:采样一段时间,查看调用链,确认net_rx_action、napi_poll、tcp_recvmsg等路径占比。perf stat -e node-loads,node-load-misses,node-stores,node-store-misses -a -- sleep 5:在支持 uncore 的平台上观察跨节点访存事件,判断是否存在明显的远端内存访问。
若 node-load-misses 占比高,说明大量数据来自远端节点,通常与内存绑定不当或队列/中断跨节点有关。perf 的结论需要与 numastat 互相印证,避免只看单一指标。
四、ftrace:追踪内核函数级时序
当 perf 只能告诉你“哪个函数热”,ftrace 能告诉你“函数之间隔了多久”。网络收包路径常用 function_graph tracer:
cd /sys/kernel/debug/tracing
echo function_graph > current_tracer
echo napi_poll > set_ftrace_filter
echo 1 > tracing_on
# 触发流量后
echo 0 > tracing_on
cat trace
输出会显示 napi_poll 内部各子函数的耗时。若发现 __netif_receive_skb 到 tcp_v4_rcv 之间出现毫秒级延迟,往往意味着软中断被抢占、锁竞争或跨节点内存访问。ftrace 开销比 perf 采样大,建议只对少量函数短时开启。
五、DPDK telemetry 与网卡统计:用户态与硬件侧证据
DPDK 绕过内核协议栈后,perf 和 ftrace 看不到收包路径,此时要用 telemetry。通过 dpdk-telemetry.py 或 socket 接口可读取 /ethdev/xstats、/ethdev/stats、/mempool 等端点,观察 rx_good_packets、rx_missed_errors、rx_nombuf、mempool 可用对象数。若 rx_missed_errors 增长,说明硬件队列满而软件未及时取走;若 rx_nombuf 增长,说明 mbuf 耗尽,需要扩大 mempool 或检查 NUMA 节点上的内存分配。
普通内核路径则用 ethtool -S eth0 查看每队列统计,如 rx_queue_0_packets、rx_queue_0_drops、tx_queue_0_restart。若只有 queue 0 有流量而其他队列为 0,说明 RSS 未生效或多队列未绑定中断,需要检查 ethtool -L 和 /proc/interrupts 的中断分布。
六、组合使用与判断依据
实际排障时,建议按“先看硬件计数,再看节点分布,最后看内核路径”的顺序收敛。一个典型示例:某 2 节点服务器上,ethtool -S eth0 显示 queue 0 的 rx_drops 持续增长,而其他队列为 0;/proc/interrupts 显示 queue 0 中断全部落在 node 1 的 CPU 8 上,但网卡位于 node 0。此时 numastat 显示 node 1 的 numa_miss 上升,perf 的 node-load-misses 也偏高。结论是:中断亲和与网卡所在节点不一致,导致跨节点访存和单队列过载。处理方式是重新设置 /proc/irq/<irq>/smp_affinity,把 queue 0 中断绑到 node 0 的 CPU,并确认 RSS 已启用多队列。处理后 queue 0 的 drops 停止增长,各队列流量趋于均衡,numastat 的 numa_miss 不再上升。
perf top 粗看,再针对性采样。--membind 是硬绑定,若目标节点内存不足会直接失败;生产环境更常用 --preferred 或 --interleave 作为折中。修改中断亲和前先记录原始值,便于回滚。六类工具的分工可以概括为:numactl 决定“放哪里”,numastat 回答“实际放哪里”,perf 回答“时间花在哪”,ftrace 回答“路径有多长”,DPDK telemetry 回答“用户态队列和内存池是否健康”,网卡统计回答“硬件队列是否丢包”。只有把节点距离、内存分布、CPU 采样、函数时序、用户态指标和硬件计数放在同一张证据链上,才能对 NUMA 相关网络性能问题给出可验证的判断。