2014 年 Alexei Starovoitov 和 Daniel Borkmann 把 eBPF 合入 Linux 3.18 时,eBPF 只有一个程序类型(socket filter)、两种 map(hash 和 array)、一个几百行的 verifier。12 年后的今天,eBPF 子系统覆盖了内核的几乎所有角落:网络(XDP / TC / sockmap)、追踪(kprobe / tracepoint / fentry / uprobe)、安全(LSM BPF)、调度(sched_ext)、存储(BPF-based I/O),甚至进入了 Windows 内核和 IETF 标准化流程。
本文不是一篇 “eBPF 远景展望”——那会沦为猜测。本文基于内核代码的演进记录、合并到主线的提交、IETF 草案的现有内容、BPF 维护者的公开邮件讨论,整理 eBPF 的过去十年、当前状态、以及正在内核实操中落地的能力扩展方向。
一、十年演进:3.18 到 6.x 的关键节点
1.1 时间线
timeline
title eBPF 内核演进关键节点
2014 : 3.18: eBPF 合入, hash/array maps
2016 : 4.7: XDP 合入
: 4.9: cgroup BPF 程序类型
2017 : 4.14: map-in-map (hash of maps)
: 4.18: BTF 格式合入
2019 : 5.0: spinlock for BPF maps
: 5.3: bounded loops (verifier)
: 5.5: BPF trampoline, fentry/fexit
2020 : 5.7: BPF LSM
: 5.8: CAP_BPF split from CAP_SYS_ADMIN
2021 : 5.11: bpf_ringbuf, struct_ops improved
: 5.13: kernel function (kfunc) calls
: 5.15: bloom filter map
2022 : 5.19: dynptr (dynamic pointers)
2023 : 6.0: bpf_mem_alloc (BPF memory allocator)
: 6.4: bpf_refcount, bpf_rb_root (linked list/rbtree)
2024 : 6.8: bpf_token, user-defined kfuncs
: 6.9: BPF arena (large contiguous memory)
2025 : 6.12: sched_ext 合入
1.2 每个节点的 “解锁了什么”
| 版本 | 特性 | 解锁的能力 |
|---|---|---|
| 3.18 | eBPF ISA + verifier + maps | 内核安全可编程的基础 |
| 4.7 | XDP | 驱动级高速包处理(DDoS 过滤、负载均衡) |
| 4.9 | cgroup BPF | 基于进程组的网络/资源策略 |
| 4.14 | map-in-map | 动态管理 map 集合(如每个容器一个 map) |
| 4.18 | BTF | 类型信息——CO-RE 的前提 |
| 5.0 | bpf_spin_lock | BPF 程序的并发控制 |
| 5.3 | bounded loops | verifier 接受带明确上界的循环 |
| 5.5 | trampoline + fentry/fexit | 零开销内核追踪 |
| 5.7 | BPF LSM | 可编程内核安全策略 |
| 5.8 | CAP_BPF | 细粒度 BPF 权限(容器安全) |
| 5.11 | bpf_ringbuf | 高性能事件缓冲(替代 perf buffer) |
| 5.13 | kfunc | BPF 调用内核函数(突破 helper 限制) |
| 5.15 | bloom filter map | 空间高效的概率性成员测试 |
| 5.19 | dynptr | 动态大小指针(verifier 可跟踪) |
| 6.0 | bpf_mem_alloc | BPF 动态内存分配 |
| 6.4 | rbtree / linked list | BPF 程序中的内核数据结构 |
| 6.8 | bpf_token | 权限委派(非特权 BPF 的安全替代) |
| 6.9 | BPF arena | GB 级虚拟内存(大型数据结构) |
| 6.12 | sched_ext | 可编程内核调度器 |
从这张表可以看出 eBPF 的演进方向:从 “用 BPF 做包过滤” 到 “用 BPF 做任何内核定制”。每一次演进都在降低 BPF 程序与 “原生内核代码” 之间的能力差距。
二、主线维护者的开发节奏与策略
2.1 核心维护者
eBPF 子系统的主要维护者(来自 MAINTAINERS
文件):Alexei Starovoitov
<ast@kernel.org>、Daniel Borkmann
<daniel@iogearbox.net>、Andrii Nakryiko
<andrii@kernel.org>。邮件列表地址
bpf@vger.kernel.org。两个 Git
树:bpf.git(修复)和
bpf-next.git(新特性)。
- Alexei Starovoitov(Meta):eBPF 的原始设计者和主要架构师。关注 verifier 安全性、指令集设计和整体走向。
- Daniel Borkmann(Isovalent / Cisco):网络相关 BPF(XDP、TC、sockmap)和 JIT 后端的核心贡献者。
- Andrii Nakryiko(Meta):libbpf、BTF、CO-RE、skeleton 机制的创建者。
2.2 bpf-next 的工作流
eBPF 子系统的开发使用两个 Git 树:
bpf树:bug 修复,逐个合入 Linus 的rc版本bpf-next树:新特性,在合并窗口期一次性合入
合并策略的关键规则:
- 每个新的 helper/kfunc 必须有明确的使用场景——不能 “因为可能有用” 而添加
- 每个新特性必须有 selftest
覆盖——
tools/testing/selftests/bpf/中的测试必须通过 - verifier 变更必须有安全分析——任何可能引入 verifier 绕过的变更会被严格审查
- 性能回归不允许——
bpf自测套件包括性能基准测试
2.3 合并统计数据
基于内核 git 历史的不精确统计:
- 每年合并窗口 eBPF 子系统的 PR 约 50–80 个
- 每个合并窗口约有 150–250 个独立提交
- 活跃贡献者(每年超过 5 个提交)约 30–40 人
- 来自 Meta、Isovalent(Cisco)、Google、Red Hat 的贡献占约 70%
三、eBPF Foundation 与标准化
3.1 组织架构
eBPF Foundation 成立于 2021 年,隶属于 Linux Foundation。成员公司包括:
- Meta(Facebook)、Google、Isovalent(Cisco)、Microsoft、Netflix、SUSE、Red Hat
工作组的划分:
| 工作组 | 职责 | 关键产出 |
|---|---|---|
| 标准化工作组 | BPF ISA 标准化 | IETF BPF ISA 草案 |
| 安全工作组 | verifier 安全审计、最佳实践 | 安全白皮书、CVE 分析 |
| 应用工作组 | 推广 BPF 在行业的应用 | 案例研究、白皮书 |
| 教育工作组 | 培训材料、文档 | eBPF 文档站点 |
3.2 IETF BPF ISA 标准化的进展
IETF draft-ietf-bpf-isa 的目标是将 BPF
指令集定义为与平台无关的国际标准。截至 2026 年:草案已提交到
IETF;一致性测试套件(bpf_conformance)可用;Linux
eBPF 和 ubpf 已通过大部分一致性测试;Windows eBPF
的一致性测试正在进行中。
标准化的关键挑战:
- Linux eBPF 是 “活” 的实现:新指令和特性持续添加到内核中,标准化需要跟上其演进速度
- 向后兼容性:BPF ISA 必须保留 “未定义行为” 的空间,以便现有程序不被破坏
- 多实现验证:需要至少两个独立实现来验证标准的可行性(Linux + ubpf 满足)
3.3 eBPF Foundation 的战略意义
eBPF Foundation 的存在本身是一个信号:eBPF 不再只是 Linux 内核的一个子系统。它正在被定位为 “系统级可编程的通用平台”——类似于 TCP/IP 或 TLS 的标准化,使得不同操作系统上的 BPF 实现可以互操作。
四、关键技术趋势
4.1 kfunc 生态:BPF 调用内核函数
kfunc(kernel function)从 5.13 开始引入,允许 BPF
程序直接调用标注了 __bpf_kfunc
的内核函数,而不是通过固定的 helper 列表:
/* kernel/bpf/helpers.c */
__bpf_kfunc void bpf_obj_new_impl(u64 local_type_id__k, void *meta__ign)
{
/* BPF 内存分配器的 kfunc */
}
/* BPF 程序使用 kfunc */
struct my_data *d = bpf_obj_new(typeof(*d));kfunc 不同于 helper: - helper 通过固定的函数 ID 调用,数量有限(~200) - kfunc 通过 BTF 类型信息找到,可以调用任何标注的内核函数 - kfunc 可以有任意签名,不限于 helper 的参数约定 - verifier 通过 BTF 信息验证 kfunc 的参数类型
4.2 dynptr:动态大小指针
dynptr 从 5.19 开始引入,解决了 BPF 程序访问变长数据的核心限制——verifier 无法验证动态大小的内存访问:
/* dynptr 允许 verifier 跟踪缓冲区大小 */
struct bpf_dynptr ptr;
bpf_dynptr_from_mem(data, size, 0, &ptr);
/* verifier 知道 ptr 的大小是 size */
bpf_dynptr_read(dst, 16, &ptr, 0); /* 安全:检查 16 <= size */dynptr 的关键贡献: - verifier 可以跟踪动态大小(不需要编译期常量) - 支持切片(slice)语义:从缓冲区中提取子范围 - 与 ring buffer、skb 等内核数据结构的集成
4.3 BPF Arena:大型虚拟内存(6.9+)
BPF Arena 是 6.9 引入的最激进的 BPF 内存扩展——它为 BPF 程序映射 GB 级的虚拟地址空间:
/* 伪代码:BPF Arena 概念示意(6.9+,完整 API 见内核 selftest)*/
struct {
__uint(type, BPF_MAP_TYPE_ARENA);
__uint(map_flags, BPF_F_MMAPABLE);
__uint(max_entries, 1ULL << 32); /* 虚拟空间大小 */
} arena SEC(".maps");
/* 实际访问需通过 bpf_arena_read_elem / bpf_arena_write_elem 等 kfunc,
* 或用户态 mmap 后与 BPF 共享;不能将 map 符号直接当指针算术使用 */Arena 的使用场景: - 大型查询表(路由表、连接跟踪表)无需按 hash map 的限制分片 - 大型缓存和临时存储 - 无需通过 map lookup 即可访问的共享内存
4.4 BPF 异常处理(RFC 阶段)
BPF 的异常处理提案允许 BPF 程序使用 try-catch 语义:
/* 提案中的 BPF 异常处理(未合入主线)*/
__try {
*ptr = value; /* 可能越界 */
} __catch {
bpf_printk("error\n"); /* 捕获异常 */
}这改变了 BPF 错误处理的根本模式——从 “提前检查所有边界” 到 “执行并捕获失败”。
五、竞争与共进化
5.1 eBPF vs 内核模块
| 维度 | eBPF | 内核模块 |
|---|---|---|
| 安全性 | verifier 静态分析保证 | 无——模块可以做任何事 |
| 开发语言 | 受限的 C(BPF 目标) | 完整的 C(含内联汇编) |
| 可访问 API | helper + kfunc(受限) | 所有内核函数和数据结构 |
| 部署 | 热加载(无需重启) | 热加载(但危险,通常重启) |
| 风险 | 加载失败不影响内核 | 模块 bug = 内核崩溃 |
| ABI 稳定性 | 通过 CO-RE 解决 | 不保证(需要重新编译) |
| 实时性 | 不可(verifier 限制) | 可(完整控制) |
| 硬件驱动 | 不可 | 必须(唯一方式) |
趋势:eBPF 正在吞并内核模块的传统用例——追踪、网络策略、安全、调度。内核模块保留的唯一不可替代领域是硬件驱动。对于纯软件的定制需求,eBPF 的安全性优势使其成为首选。
5.2 XDP vs DPDK
| 维度 | XDP | DPDK |
|---|---|---|
| 模型 | 内核集成(NAPI poll) | 完全用户态(绕过内核) |
| 峰值吞吐 | 高(单核可达数十 Mpps 量级,依硬件与模式;见 DPDK/XDP 社区 benchmark,非本站实测) | 极高(用户态独占 CPU + PMD,社区 benchmark 通常高于 XDP,非本站实测) |
| 内核集成 | 完整(iptables/routing/sockets) | 无(需自行实现协议栈) |
| 部署 | 标准内核 + BPF 程序 | 专用 PMD + 独占 CPU + 大页 |
| 灵活性 | 可以与现有网络栈协作 | 全部或全无 |
趋势:XDP + AF_XDP 的组合在 “足够高的性能 + 保留内核集成” 这个卖点上与 DPDK 竞争。对于绝大多数场景(CDN、负载均衡、防火墙),XDP 的性能已经足够。DPDK 的领地缩小到需要极致吞吐的场景(电信核心网、高频交易)。
5.3 eBPF vs WebAssembly
| 维度 | eBPF | WebAssembly |
|---|---|---|
| 目标 | 内核级安全可编程 | 应用级安全沙箱 |
| 运行时 | 内核态 | 用户态(通常) |
| ISA | 专用(CISC-ish,64-bit 固定) | 通用(栈式机,32/64-bit) |
| 验证 | 抽象解释 + DFA | 类型检查(验证阶段) |
| 内存模型 | 受限(maps + stack) | 线性内存 + 边界检查 |
| 生态 | C / Rust(编译到 BPF) | 多种语言(编译到 Wasm) |
重叠领域:两者都提供了 “安全沙箱 + 字节码 VM” 的模式。BPF 在内核领域没有竞争——Wasm 不能在内核中运行。在用户态,Wasm 是更通用的沙箱,但 BPF 的某些特性(CO-RE、map 类型、内核集成)在系统级场景中有独特优势。
六、未来 5 年的可能走向
以下判断基于已经出现在邮件列表或 bpf-next 中的 RFC 提案,而非凭空猜测。
短期(1–2 年):
- kfunc 生态成熟:更多内核子系统暴露 kfunc,BPF 程序可以调用来自调度器、文件系统、内存管理、块设备层的函数
- BPF arena 稳定:大型 BPF 数据结构成为常规用法
- sched_ext 生态建立:出现 5–10 个生产级的领域专用 BPF 调度器
- BPF_TOKEN 普及:非特权 BPF 通过 token 委派在容器化环境中广泛使用
中期(3–5 年):
- BPF ISA 成为 IETF RFC:正式的发布标准
- 跨平台 BPF 工具链:同一套 BPF 程序在 Linux 和 Windows 上运行(有限场景)
- BPF 异常处理合入:try-catch 成为 BPF 程序的标准错误处理方式
- 更多内核子系统通过 struct_ops 暴露可编程接口
长期(5+ 年):
- eBPF 成为 “内核扩展的默认接口”:大多数需要内核定制的场景优先使用 eBPF 而非内核模块
- LLM 辅助 BPF 程序生成可能降低简单场景的编写门槛,但 verifier 约束和内核 API 特殊性决定了它难以替代人工审查。
七、如何在社区中跟踪 BPF 演进
# 订阅 BPF 邮件列表
echo "subscribe bpf" | mail majordomo@vger.kernel.org
# 跟踪 bpf-next 的合并窗口 PR
git log --oneline v6.6..v6.7 -- kernel/bpf/ include/linux/bpf*.h
# 阅读 bpf 维护者的 LPC/Plumbers 演讲
# 演讲通常在 YouTube 上公开
# 跟踪 eBPF Foundation 的更新
# https://ebpf.foundation
# 使用 bpftool 检查当前内核的 BPF 特性
bpftool feature probe八、总结
eBPF 从 3.18 的一个 socket filter 虚拟机,演变为覆盖内核几乎所有子系统的通用可编程平面。这个演进由三个力量驱动:
- 内核维护者的严格把关——每个新特性必须有清晰的使用场景、完整的测试覆盖和安全分析
- 工业界的实际需求——Meta 的 tracing、Cilium 的网络、Netflix 的性能分析,推动了 BPF 能力边界的持续扩展
- 标准化的前瞻布局——eBPF Foundation 和 IETF 的工作确保 BPF 不只是 “Linux 一时兴起的特性”,而是跨平台的长期标准
eBPF 的内核实现仍然在快速演进。跟踪 bpf-next 的提交、阅读维护者在 LKML 的讨论、关注 eBPF Foundation 发布的标准文档,是保持对这项技术前沿理解的最佳方式。
本系列的前 20 篇涵盖了 eBPF 从指令集到调度器的完整内核实现。希望这套系统性拆解能为你阅读 BPF 内核源码、排障 verifier 错误、或设计自己的 BPF 方案提供扎实的基础。
参考
- Linux 内核源码
kernel/bpf/:BPF 子系统的核心代码 - Linux 内核源码
bpf-next树:最新特性开发 - IETF BPF ISA 草案
- eBPF Foundation
- LKML BPF 邮件列表归档:lore.kernel.org/bpf
- BPF 内核自测
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【eBPF 内核实现深度拆解】BPF 指令集解码:寄存器机器、调用约定与指令编码
从 eBPF 虚拟机的 11 个 64-bit 寄存器和 struct bpf_insn 出发,逐条拆解 ALU64/ALU32、跳转、加载存储、call 四类指令的字段语义与编码格式,建立后续 verifier 和 JIT 讨论的精确基础。
【eBPF 内核实现深度拆解】验证器框架:从 BPF_PROG_LOAD 到 do_check()
跟踪 BPF_PROG_LOAD 系统调用的内核执行路径,逐层拆解 bpf_prog_load()→bpf_check()→do_check_main() 的调用链,建立 verifier 执行全景——这是理解 verifier 安全保证的入口。
【eBPF 内核实现深度拆解】JIT 编译器后端:x86-64 与 ARM64 的 BPF→Native 翻译管线
从 bpf_jit_compile() 入口出发,拆解 BPF 字节码到 x86-64/ARM64 本地指令的翻译过程——寄存器映射策略、ALU 指令的 one-to-one/many-to-one 翻译、尾调用与 call 的本地实现、JIT 镜像的 kallsyms 集成,以及 JIT 与 interpreter 的性能边界。
【eBPF 内核实现深度拆解】Map 内核实现(上):hash / array / per-CPU 的数据结构与并发模型
从 bpf_map_ops 虚函数表出发,逐层拆解 BPF_MAP_TYPE_HASH、BPF_MAP_TYPE_ARRAY、per-CPU 变体的内核实现——htab 的 bucket 链表与 prealloc、bpf_array 的零拷贝共享、per-CPU 分配器的无锁语义。