- eBPF
- 可观测性
- 性能剖析
- 网络
【免费下载链接】bcc
BCC - Tools for BPF-based Linux IO analysis, networking, monitoring, and more
libbpf-tools 是 BCC 仓库中以 libbpf + CO-RE(Compile Once, Run Everywhere)方式重写的 Linux 可观测性工具集,覆盖文件系统、网络、调度、内存、安全等 58 个场景。本文围绕 libbpf-tools/README.md 展开,完整讲解其构建流程、源码组织约定、vmlinux.h 生成机制、bpftool 骨架代码生成,以及在有/无 BTF 内核上的两种部署路径,并结合仓库源码逐层剖析实现原理。读完本文,你将能独立编译整套 libbpf-tools、新增一个符合约定的工具,并为缺少 BTF 信息的内核生成最小 CO-RE 支持。
一、libbpf-tools 是什么:BCC 的 libbpf 原生实现
BCC 传统的 Python 工具在运行时依赖 Clang/LLVM 将 BPF C 源码编译为字节码,存在编译开销大、内核头文件依赖重的问题。libbpf-tools 则把 BPF 程序预编译进 ELF 文件,运行时通过轻量级用户态库 libbpf 加载,并借助 BPF CO-RE 技术实现"一次编译、处处运行"——同一份字节码可以通过 BTF 类型信息在不同内核版本上完成重定位。
整个工具集位于仓库的 libbpf-tools 目录,包含:
- 58 个独立工具:由 libbpf-tools/Makefile 中的
APPS变量统一登记,如bashreadline、opensnoop、biolatency、tcpconnect、runqlat、profile、offcputime等,另有futexctn、memleak、opensnoop三个工具(BZ_APPS)额外依赖 blazesym 进行栈符号化; - 17 个命令别名:
FSDIST_ALIASES(btrfsdist、ext4dist、xfsdist等 8 个)、FSSLOWER_ALIASES(btrfsslower、ext4slower等 8 个)和SIGSNOOP_ALIAS(killsnoop),它们通过符号链接复用fsdist、fsslower、sigsnoop的二进制; - 跨架构支持:
x86、arm64、powerpc、riscv、s390、loongarch六个架构目录,各含预生成的vmlinux.h; - 共享辅助库:
trace_helpers、syscall_helpers、errno_helpers、map_helpers、uprobe_helpers、btf_helpers、compat、path_helpers等,编译时打包进COMMON_OBJ。
二、构建 libbpf-tools:一条 make 命令打通全部工具
2.1 基本构建与产物布局
在 libbpf-tools 目录下直接运行make即可构建全部工具:
cd libbpf-tools make构建产物的布局规则(对应 Makefile 中的OUTPUT := $(abspath .output)):
- 所有中间产物(
.o目标文件、.bpf.oBPF ELF、.skel.h骨架头文件、静态库libbpf.a)默认全部落入.output子目录,源码与构建产物完全隔离; - 唯一例外是最终可执行文件,直接生成在当前目录(
libbpf-tools/下); make clean会清除.output目录及当前目录下生成的所有二进制(Makefile)。
由于 libbpf 系统包并非在所有发行版上都可用,所有工具静态链接BCC 依赖的 libbpf 版本(来自 src/cc/libbpf 子模块),最终二进制只保留极少量动态依赖——libc、libelf和libz(Makefile 中链接-lelf -lz)。如果构建因 libbpf 子模块过旧而失败,先执行:
git submodule update --init --recursive2.2 可定制的构建变量
Makefile 暴露了若干可覆盖变量,供交叉编译与特殊场景使用:
| 变量 | 默认值 | 说明 |
|---|---|---|
CLANG | clang | 编译 BPF C 代码的编译器 |
LLVM_STRIP | llvm-strip | 剥离 BPF ELF 调试符号的工具 |
ARCH | 由uname -m自动推导 | 映射规则见 Makefile:x86_64→x86、aarch64→arm64、ppc64le→powerpc、riscv64→riscv、s390x→s390、loongarch*→loongarch、mips*→mips;不支持的架构会直接报错 |
CROSS_COMPILE | 空 | 交叉编译前缀,通过allow-override机制作用于CC/LD |
V=1 | 关闭 | 打开详细编译日志 |
EXTRA_CFLAGS/EXTRA_LDFLAGS | 空 | 追加编译/链接参数 |
prefix | /usr/local | make install的安装前缀 |
ENABLE_MIN_CORE_BTFS | 未定义 | 开启无 BTF 内核支持(见第六节) |
BTF_HUB_ARCHIVE | $(abspath btfhub-archive) | btfhub-archive 本地仓库路径 |
一个典型的多核并行构建:
make -j$(nproc)安装到系统(默认/usr/local/bin,可用prefix/DESTDIR调整):
make install三、工具源码组织约定:三个文件构建一个工具
3.1 命名规范
libbpf-tools 中的工具遵循严格的命名约定(README 第三节):
<tool>.c:工具的用户态 C 代码,负责解析参数、打开/加载 BPF 对象、挂载探针、轮询事件并输出结果;<tool>.bpf.c:BPF 内核态 C 代码,编译为 BPF ELF 文件(.bpf.o),再由该 ELF 生成 BPF 骨架头文件<tool>.skel.h,最终被<tool>.c包含;<tool>.h(可选):BPF 侧与用户态共享的类型与常量定义。
以bashreadline为例(该工具用于打印所有 bash 进程中用户输入的命令行):
- bashreadline.h 定义了共享结构
struct str_t(pid+ 最长 80 字节的命令字符串); - bashreadline.bpf.c 第一行即
#include <vmlinux.h>,随后用SEC("uretprobe/readline")+BPF_URETPROBE宏定义探针,从 readline 返回地址bpf_probe_read_user_str读出用户输入,过滤非 bash 进程后经bpf_perf_event_output上报; - bashreadline.c 包含
bashreadline.skel.h,通过bashreadline_bpf__open_opts()、bashreadline_bpf__load()加载骨架,用bpf_program__attach_uprobe()挂载,再用perf_buffer__new()/perf_buffer__poll()消费事件。
3.2 加入新工具:只需一行
对遵循上述约定的简单工具,只要把<tool>名字追加到 Makefile 的APPS变量中,即可随其他工具一同构建。构建链路由三条通用规则驱动(Makefile):
%.bpf.o <- clang -target bpf 编译 %.bpf.c(包含 $(ARCH)/vmlinux.h) %.skel.h <- bpftool gen skeleton %.bpf.o 生成骨架 %.o <- gcc 编译 %.c(依赖 %.skel.h)编译 BPF 时使用-D__TARGET_ARCH_$(ARCH)宏区分架构,并在链接前用llvm-strip -g剥离调试信息。对更复杂的应用(例如依赖额外库或多步构建),建议把应用放入独立子目录,再从主 Makefile 链接引入。
注意一个特例:softirqs使用更强的-mcpu=v3指令集编译(Makefile 与 L207),说明不同工具可以通过覆盖BPFCFLAGS定制 BPF 编译选项。
四、bpftool 与骨架(skeleton)代码生成
bpftool 是 BPF 资源检视与辅助工具的通用入口,其"骨架代码生成"功能是 libbpf-tools 加载/交互 BPF 程序的核心依赖。由于 bpftool 系统包尚未在所有发行版广泛可用,仓库直接在libbpf-tools/bpftool子目录内置了其源码,构建时在.output/bpftool下先编译出bootstrap/bpftool(Makefile),再通过bpftool gen skeleton把.bpf.o转成 C 骨架头文件(Makefile)。
骨架头文件的典型用法在 bashreadline.c 中清晰可见:
obj = bashreadline_bpf__open_opts(&open_opts); obj = bashreadline_bpf__open_opts(&open_opts); err = bashreadline_bpf__load(obj);生成的骨架为每个工具提供xxx_bpf__open / open_opts / load / destroy全套生命周期函数,用户态无需手写bpf(BPF_PROG_LOAD)等繁琐的底层调用。
五、vmlinux.h:预生成、版本化、随仓库分发
5.1 为什么需要 vmlinux.h
vmlinux.h包含内核的全部类型定义(既含导出类型,也含仅内部可见的类型)。CO-RE 应用必须在 BPF 程序源码中 include 该文件,从而摆脱对内核头文件包的依赖。
5.2 版本化与符号链接
为保证构建可复现,vmlinux.h预生成并随源码入库,而不是在构建机上现场生成——因为其生成依赖带有 BTF 信息的内核(即CONFIG_DEBUG_INFO_BTF=y),构建服务器未必满足条件。生成基准是上游 Linux 的特定小版本 tag:例如vmlinux_505.h来自 v5.5 tag,编译时使用默认配置并额外开启CONFIG_DEBUG_INFO_BTF=y。
由于不同内核版本的类型定义可能不兼容,头文件按<MAJOR><MINOR>后缀版本化,并始终存在一个符号链接vmlinux.h指向默认版本(通常是最新)。在 libbpf-tools/x86 目录中可以实际看到:
vmlinux.h -> vmlinux_614.h即当前默认版本来自内核 6.14(vmlinux_614.h,约 3.4MB)。arm64、powerpc、riscv、s390、loongarch各架构目录均保持同样的组织方式。编译 BPF 时 Makefile 会把-I$(ARCH)/加入包含路径,使#include <vmlinux.h>命中正确架构的版本化头文件。
六、内核侧前提:CONFIG_DEBUG_INFO_BTF=y
6.1 检测机制
libbpf 在加载时探测系统是否导出/sys/kernel/btf/vmlinux(内核 5.5+),或系统中是否存在带 BTF 信息的 ELF 版本。注意:ELF 文件存在并不代表其中包含 BTF 信息。内核 Kconfig 需满足:
CONFIG_DEBUG_INFO_BTF=y CONFIG_DEBUG_INFO=y6.2 重新编译内核的要点
- 在
.config中开启上述两个选项; - 内核构建期间必须存在pahole 1.13(建议 1.16+),它来自
dwarves包。缺少 pahole 时 BTF 不会被生成;在较老的内核上只会得到警告,内核仍能成功构建,但/sys/kernel/btf/vmlinux不会出现。
七、在无 BTF 内核上运行:BTFGen + BTFHub 最小化方案
许多发行版内核并未开启CONFIG_DEBUG_INFO_BTF=y,此时/sys/kernel/btf/vmlinux不存在。但 libbpf-tools 仍可通过BTFGen 与 BTFHub运行:为最常见的 Linux 发行版生成精简 BTF 文件,随工具分发,加载 eBPF 程序时提供 CO-RE 重定位所需的全部信息。
7.1 开启方式
没有本地 btfhub-archive 时,make会自动克隆该仓库(默认到$HOME/.local/share目录):
make ENABLE_MIN_CORE_BTFS=1 -j$(nproc)已有本地副本时,通过BTF_HUB_ARCHIVE指定路径以复用、避免重复克隆:
make ENABLE_MIN_CORE_BTFS=1 BTF_HUB_ARCHIVE=<path_to_btfhub-archive> -j$(nproc)7.2 实现链路剖析
开启ENABLE_MIN_CORE_BTFS后,构建系统额外执行 Makefile.btfgen:
- 从 btfhub-archive 按当前
ARCH匹配所有*.btf.tar.xz完整 BTF 文件(对应各发行版各内核版本); - 对每一个完整 BTF,用
bpftool gen min_core_btf <完整btf> <精简btf> $(OUTPUT)/*.bpf.o结合全部工具的.bpf.o生成最小精简 BTF——只保留工具实际用到的那部分类型; - 将全部精简 BTF 压缩为
min_core_btfs.tar.gz,再用ld -r -b binary以二进制对象形式嵌入每个工具可执行文件(生成min_core_btf_tar.o,见 Makefile 与 Makefile.btfgen)。
运行时,工具通过 btf_helpers.c 中的ensure_core_btf()完成"按需解包":
- 若系统已提供
/sys/kernel/btf/vmlinux(vmlinux_btf_exists()),直接返回,不做任何额外工作; - 否则读取
/etc/os-release与uname(),拼出./<ID>/<VERSION_ID>/<arch>/<kernel-release>.btf形式的文件名; - 用 zlib
inflate解压内嵌的min_core_btfs.tar.gz(btf_helpers.c),按 tar 格式定位对应文件,写入/tmp/bcc-libbpf-tools.btf.XXXXXX临时文件; - 将临时文件路径赋给
opts->btf_custom_path,交给xxx_bpf__open_opts()使用(btf_helpers.c);程序退出时由cleanup_core_btf()删除临时文件。
以 bashreadline.c 为例,工具主流程会先调用ensure_core_btf(&open_opts),失败则报错退出,成功后再打开骨架对象。若编译时未开启ENABLE_MIN_CORE_BTFS且系统无 BTF,ensure_core_btf()返回-EOPNOTSUPP,工具会在启动时给出明确提示(btf_helpers.c)。
八、实战:在目标机器上跑通一个工具
完成构建后,任意一个工具都是无依赖包袱的独立二进制。以 bash 命令捕获为例:
# 在 libbpf-tools 目录构建后直接运行 ./bashreadline输出三列表格(时间 / PID / 命令)。其用户态实现会先解析参数(-s指定 libreadline.so 路径、-v开启 verbose),随后自动探测 readline 位置:优先在/bin/bash内查找readline符号,找不到则通过ldd /bin/bash定位libreadline.so(bashreadline.c)。在新版 bash 中符号名可能为readline_internal_teardown,工具会通过解析 ELF 符号表自动选择(bashreadline.c)。
其余工具的使用方式与 BCC 同名工具对齐,均以-h查看帮助;帮助文本、-v详细日志等统一基于 GNU argp 解析(如 bashreadline.c 的argp_option定义),阅读任一工具的用户态代码即可掌握范式。
九、进一步深入
- 源码入口:工具全集见 libbpf-tools 目录;构建规则见 Makefile 与 Makefile.btfgen;无 BTF 内核的运行时解包逻辑见 btf_helpers.c。
- 参考文章:README 推荐了三篇进阶材料——Facebook 的 "BPF Portability and CO-RE"(CO-RE 背景)、"HOWTO: BCC to libbpf conversion"(BCC 到 libbpf 的逐例移植教程)以及 PingCAP 的 "Tips & tricks for writing libbpf-tools"(编写技巧),可用于理解 CO-RE 重定位与移植方法论。
- 概念对照:传统 BCC Python 工具位于 tools 目录,二者可对照同名工具(如
tools/opensnoop.py与libbpf-tools/opensnoop),体会从运行时编译到预编译 + CO-RE 的演进;本目录工具的测试用例见 tests 目录。
- eBPF
- 可观测性
- 性能剖析
- 网络
【免费下载链接】bcc
BCC - Tools for BPF-based Linux IO analysis, networking, monitoring, and more
相关推荐
Linux 内核 BPF 开发实战:libbpf 架构总览、对象骨架与 CO-RE 可移植性机制
Linux 内核 BPF 开发实战:libbpf 架构总览、对象骨架与 CO RE 可移植性机制 本文基于 Linux 内核仓库中的 Documentation
操作系统内核驱动驱动开发虚拟化嵌入式网络存储Linux 内核 libbpf 技术指南:BPF 应用生命周期、Skeleton、CO-RE 与构建体系
Linux 内核 libbpf 技术指南:BPF 应用生命周期、Skeleton、CO RE 与构建体系 本文基于 Linux 内核源码树中 Documenta
操作系统内核驱动驱动开发虚拟化嵌入式网络存储Video2X终极指南:三步让老旧视频焕发4K新生的免费AI神器
Video2X终极指南:三步让老旧视频焕发4K新生的免费AI神器 你是否曾经翻出过那些模糊的老旧家庭录像,看着那些像素化的画面感到遗憾?或者下载了一些经典影片,
eBPF可观测性性能剖析网络
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考