eBPF 从包过滤器进化成内核的通用可编程框架——networking、tracing、security、scheduling 全覆盖。
一、先看图
flowchart TD
SRC[BPF C 源码] --> CLANG[clang -target bpf]
CLANG --> OBJ[BPF 字节码<br/>.o ELF]
OBJ --> LOAD[bpf syscall<br/>BPF_PROG_LOAD]
LOAD --> VERIFY[verifier<br/>安全检查]
VERIFY --> JIT[JIT 编译<br/>→ 本机码]
JIT --> ATTACH[attach 到 hook<br/>kprobe/tracepoint/XDP...]
ATTACH --> MAP[BPF maps<br/>用户态 ↔ 内核态通信]
classDef build fill:#388bfd22,stroke:#388bfd,color:#adbac7;
classDef kern fill:#3fb95022,stroke:#3fb950,color:#adbac7;
class SRC,CLANG,OBJ build
class LOAD,VERIFY,JIT,ATTACH,MAP kern
二、程序类型
| 类型 | 挂载点 | 用途 |
|---|---|---|
| kprobe | 内核函数 | 追踪 |
| tracepoint | 静态探针 | 追踪 |
| XDP | 网卡入口 | 高速包处理 |
| tc | 流量控制 | 网络策略 |
| cgroup | cgroup 钩子 | 资源控制 |
| LSM | 安全钩子 | 访问控制 |
| sched_ext | 调度器 | 自定义调度 |
| struct_ops | 内核回调结构 | 替换内核函数指针 |
三、verifier
bpf(BPF_PROG_LOAD, ...) → verifier 模拟执行所有路径
verifier 检查:
- 无越界内存访问
- 无无限循环(有界循环 6.x 允许)
- 所有路径都返回
- 辅助函数参数类型正确
- 不泄露内核指针到用户态
四、JIT
verifier 通过 → JIT 编译为本机码(x86、arm64、riscv)。
sysctl net.core.bpf_jit_enable=1 # 默认开启
sysctl net.core.bpf_jit_harden=2 # 生产加固JIT 后性能接近手写内核代码。
五、BPF Maps
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, u32);
__type(value, u64);
__uint(max_entries, 10240);
} my_map SEC(".maps");常用 map 类型:
| 类型 | 用途 |
|---|---|
| HASH | 通用键值 |
| ARRAY | 索引数组 |
| PERCPU_HASH/ARRAY | per-CPU 避免竞争 |
| RINGBUF | 事件输出到用户态 |
| LPM_TRIE | 前缀匹配(路由) |
| BLOOM_FILTER | 概率判断 |
六、BTF 与 CO-RE
6.1 BTF(BPF Type Format)
内核编译时生成类型信息 → BPF 程序可以按名称访问结构体字段。
6.2 CO-RE(Compile Once, Run Everywhere)
// libbpf CO-RE
u32 pid = BPF_CORE_READ(task, tgid);编译一次 → 运行在不同内核版本 → libbpf 根据 BTF 重定位字段偏移。
七、BPF_LSM
SEC("lsm/file_open")
int BPF_PROG(restrict_open, struct file *file)
{
// 自定义安全策略
return -EPERM;
}用 BPF 实现 LSM 钩子 → 动态安全策略 → 不需要编译内核模块。
八、sched_ext
SEC("struct_ops/enqueue")
void BPF_PROG(my_enqueue, struct task_struct *p, u64 enq_flags)
{
scx_bpf_dispatch(p, SCX_DSQ_GLOBAL, SCX_SLICE_DFL, enq_flags);
}用 BPF 写调度器 → 实验性调度策略 → 无需重编内核。
九、观察
bpftool prog list # 已加载 BPF 程序
bpftool map list # 已创建 map
bpftool prog dump xlated id 1 # 查看字节码
bpftool btf dump file /sys/kernel/btf/vmlinux | head
# bpftrace 快速追踪
bpftrace -e 'kprobe:vfs_read { @[comm] = count(); }'十、小结
- eBPF = 内核通用可编程框架
- verifier 保证安全 → JIT 保证性能
- maps 实现用户态 ↔︎ 内核态通信
- BTF + CO-RE 解决跨版本兼容
- BPF_LSM + sched_ext 扩展到安全和调度
参考文献
kernel/bpf/Documentation/bpf/- Brendan Gregg, “BPF Performance Tools.” 2019
- ebpf.io
工具
bpftoolbpftracelibbpfbcc
延伸阅读
上一篇:perf 子系统 下一篇:kprobe/uprobe
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【eBPF 内核实现深度拆解】从验证器到 JIT,从 BTF 到调度器
eBPF 内核虚拟机内部实现系统讲解:BPF 指令集与寄存器机器、验证器的抽象解释与状态裁剪、JIT 编译器后端、Map 各类型的并发与内存模型、helper 函数注册与类型检查、BTF 格式规范与 CO-RE 重定位引擎、libbpf 加载器工程、fentry/fexit 蹦床机制、sched_ext 调度器内核接口。面向想读懂 eBPF 内核源码、写生产级 BPF 程序的系统工程师。
【eBPF 内核实现深度拆解】BTF 格式规范与内核类型系统
从 BTF 的二进制编码格式(btf_header + type entries + string table)出发,讲清 BTF 如何编码基本类型、结构体、联合体、函数原型与 typedef——BTF.ext 节的 func_info/line_info 记录,以及内核 pahole 的 BTF 生成与去重算法 btf_dedup。
【eBPF 内核实现深度拆解】CO-RE 重定位引擎:libbpf 的运行时指令修补
从 clang 内置函数 __builtin_preserve_access_index 出发,追踪 BPF_CORE_READ 等宏如何生成 BTF.ext CO-RE 重定位记录,再到 libbpf 加载时 bpf_core_apply_relo() 根据目标内核 BTF 计算正确字段偏移量并修补 BPF 指令——可移植 BPF 的核心引擎。
【可观测性工程】eBPF 可观测性全景:bcc、bpftrace、libbpf 的工程路径
eBPF 如何实现零侵入、内核级、低开销的可观测性:从 kprobe/uprobe/tracepoint/fentry 钩子机制,到 bcc 工具集、bpftrace 脚本语言、libbpf+CO-RE 可移植编程,再到 Pixie、DeepFlow、Grafana Beyla 等商业化工具,结合内核版本兼容性与生产部署实战。