Post

kprobe vs fentry:eBPF 追踪性能对比

kprobe vs fentry:eBPF 追踪性能对比

kprobe vs fentry:eBPF 追踪性能对比

本文介绍了 eBPF 追踪中 kprobefentry 机制的技术差异,重点解释了为什么 fentrypwru 等高性能工具的首选。

1. 概述

  • kprobe (Kernel Probe):传统的动态追踪机制。可以挂载到内核中几乎任何指令位置。
  • fentry (Function Entry):较新内核(5.5+)引入的轻量级追踪机制,专门为函数入口追踪而优化。

2. 工作原理

kprobe:“断点”法

kprobe 依赖 CPU 异常和陷阱处理。

  1. 指令替换:内核将目标函数的第一条指令替换为断点指令(如 x86 上的 int3)。
  2. 陷阱/异常:CPU 执行到此处触发异常。
  3. 上下文切换:暂停执行并切换到内核异常处理程序。
  4. 执行 Handler:查找并执行附加的 BPF 程序。
  5. 单步执行:在特殊区域运行被替换的原始指令。
  6. 恢复:继续正常流程。

性能成本:开销主要来自上下文切换异常处理,涉及全量寄存器的保存与恢复。

fentry:“蹦床”法

fentry 利用 BPF Trampoline 机制,将追踪转化为一次普通的函数调用。

  1. Nop 替换:现代内核编译时在函数入口保留了空间(通常是 nop 指令)。
  2. 直接调用:挂载 BPF 时,内核将 nop 修改为直接的 call 指令。
  3. 执行 Trampolinecall 跳转到生成的“蹦床”(一段汇编代码)。
  4. 执行 BPF:蹦床保存极少数必要寄存器,直接调用 BPF 程序。
  5. 返回:完成后恢复寄存器并返回原函数。

性能成本:开销等同于标准的 C 函数调用,完全避免了昂贵的异常机制。

3. 对比总结

特性kprobefentry
机制软件断点 (int3)直接调用 (call)
开销高 (异常 + 上下接切换)低 (函数调用)
延迟微秒级纳秒级
数据访问需要解析 PT_REGS直接访问寄存器/参数
内核版本极旧版本起支持较新 (Linux 5.5+)
插桩位置任意指令仅限函数入口/出口
BTF 依赖可选强制要求

4. 为什么对 pwru 很重要

pwru 通常会同时挂载成百上千个内核函数 (--all-kprobes)。

  • 使用 kprobe:处理一个网络包可能触发 10 次以上探针。如果每个探针开销巨大,整机延迟会剧增,吞吐量大幅下降。
  • 使用 fentry:单次调用开销几乎可忽略不计,使得在大规模路径追踪时不至于拖垮系统性能。

5. 结论

对于 pwru 这种大规模挂载工具,只要内核支持,fentry 永远是优选方案。它通过消除异常处理开销,比 kprobe 提供 5 到 10 倍的性能提升。

This post is licensed under CC BY 4.0 by the author.