“Less of a presentation, more of a scream of realization.”
这是 eBPF 的作者 Alexei Starovoitov 在 LSF/MM/BPF 2026 峰会上对自己演讲的开场白。
他说得没错。这场演讲不像一个技术分享,更像是一个发现"我的孩子快要被时代抛弃了"的父亲的焦虑独白。
一、AI 程序员来了,但 BPF 的门还没开
你可能已经见识过 AI 写代码的能力。
Claude、GPT、Copilot 这些工具,最擅长一个模式:写代码 → 看编译报错 → 修 → 运行 → 看结果 → 再写。编译器给的错误信息越详细,AI 修得越快。运行反馈越即时,AI 学得越好。
这就是所谓的"紧反馈循环"。
但 BPF 这个领域,恰恰把这个循环堵死了。
要写一个 BPF 程序,你得先启动一个虚拟机,把编译好的程序传进去,跑 veristat 看验证器日志——全程可能几分钟就过去了。
验证器吐出来的日志是好几 MB 的原始转储,把每个检查过的指令全 dump 一遍。人类都看到瞳孔地震,AI 更是一脸懵。
更致命的是:AI 遇到验证器拒绝,就直接当作"平台限制"放弃了。它不会像人类一样说"换个写法试试",它只会认为"这条路走不通"。
于是出现了这些魔幻现实:
某团队反馈:Claude “不擅长” BPF C 编程 另一团队说 BPF “不在模型的知识分布范围内” 还有公司直接弃用了 BPF,选了另一个叫 rex 的方案
理由就一个:验证器太难搞了。
二、Alexei 的答案:把 Rust 搬进内核
面对这个局面,Alexei 没有选择教 AI 怎么适应 BPF 的怪癖。
他选了另一条更激进的路:让 BPF 直接原生跑 Rust。
等等,之前不是有人干过这事儿吗?
是的,有个叫 Aya 的项目尝试过。但你猜怎么着——它把 Rust 的翅膀全剪了:
不能用 core::fmt—— 不能格式化输出不能用堆 —— 不能用 Vec、String、BTreeMap 不能 panic —— 不能做错误处理
说白了就是带 Rust 语法的 C。Rust 的精华全没了。
Alexei 的做法正好反过来:
移除 BPF 的限制,而不是牺牲 Rust 的特性。
三、六项手术,把 BPF 的心肺换了一遍
1. 重新设计内存模型
以前 BPF 程序的内存东一块西一块。现在所有数据都放到一个叫 arena 的扁平地址空间里。
栈的限制解除了,堆通过 #[global_allocator] 用 BPF 的 arena 分配器。你写 Rust 的时候完全感觉不到 BPF 的存在——它就是一个正常的 Rust 程序。
2. 让间接调用活过来
BPF 验证器以前对函数指针、trait 对象这些东西严防死守——“指针不能做算术”“不能解引用”。
现在全都放开了。dyn Trait 能用,函数指针能用,闭包能用。Rust 的动态派发机制完整保留。
3. 循环不死
老验证器处理循环的方式很暴力:要么展开,要么拒绝。导致很多合理的循环逻辑撞上 100 万条指令的上限。
新方案用一个叫 widening 的技术:
如果验证器看到循环变量第一次是 [5,5],第二次是 [10,10],它不会继续穷举所有可能值,而是直接拓宽成 [5,10]。如果还发散,就拓宽成 [0, U64_MAX]。通常一两次就能收敛。
复杂度从 O(循环体 × 迭代次数) 降到了 O(循环体)。
4. 放开调用约束
Rust 编译器经常生成超过 5 个参数的函数调用,也经常返回超过 8 字节的结构体——比如 Result<T, E>。
以前验证器直接拒绝。现在通过栈传递额外参数,兼容 x86 和 ARM64 的调用约定。Rust 标准库的代码不用做任何修改。
5. Panic 要有仪式感
Rust 的 panic 在 BPF 里不会静默崩溃。完整错误信息通过内核的 bpf_stream_vprintk 先打印出来:
panicked at 'index out of bounds: the len is 11 but the index is 12'然后内核优雅地遍历 BPF 调用栈,为每个帧执行 Drop 实现做清理,最后批量释放所有 arena 内存。
有尊严地死,而不是静默地烂掉。
6. 除零检查:C 和 Rust 的分水岭
这是最有意思的对比。
同样一个除法操作:
C 版 BPF: 验证器重写了每个除法——除零时静默返回 0。程序带着错误数据继续跑,神不知鬼不觉。
Rust 版 BPF: 编译器插入运行时检查——除零触发 unwinding,先打日志再优雅退出。热路径还是 5 条指令,但 panic 处理代码放在冷路径里,不影响正常性能。
翻译成人话:C 帮你隐藏 bug,Rust 帮你找到 bug。
四、到底能跑啥,不能跑啥?
好消息是:几乎所有 Rust 标准库都能在 BPF 上用了。
Vec、String、Box —— 上 arena 堆。BTreeMap —— 内核里也能用 map 了。dyn Trait、函数指针、闭包 —— 全支持。for i in 0..n、.iter().map().filter() —— 全过。Option<T>、Result<T,E>、泛型 —— 原生支持。core::fmt、Debug 格式化 —— 可以,通过间接调用走堆。
不能用的只有三样:浮点数(BPF 硬件不支持)、async/await(待评估)、std::*(只能用 no_std 子集)。
整个编译管线全用上游工具: Rust 源码 → rustc → LLVM → libbpf。没有任何自定义工具链。
五、AI Agent 的终极内核调试工具包
Alexei 还提出了一个更大胆的想法:专门给 AI agent 写一个内核调试的 SKILL.md。
这个 SKILL.md 会教 agent:
用 bpftool btf dump探索内核里有哪些类型、结构体、可以挂载的探测点自动读函数签名,生成匹配的 Rust BPF probe 用 drgn检查内存状态形成假设写 Rust BPF probe 持续监控可疑路径
这套组合拳下来,agent 不需要人类手把手教它怎么调内核。它自己就能发现哪里出了问题、该挂载什么、怎么写 probe。
他还指出:bpftrace 对人类专家很好,但对 AI 完全是灾难。 因为 bpftrace 是个小众 DSL,LLM 训练数据里见得少,幻觉率特别高。错误信息也没给修复建议,AI 卡住了就原地打转。
写在最后
演讲稿最后一页只有短短几个字:
LLMs improve fast enough. Just wait.
LLM 进步得够快了,等等 BPF。
这话从 eBPF 本人嘴里说出来,有点浪漫,也有点悲壮。他在说:我有一个做了十年的项目,它很强大,但它在 AI 时代落了伍。没关系,我给它换一套引擎。
这件事能不能做成?Alexei 的 roadmap 上 “Rust on BPF” 那一项还是 🔲 状态。
但前面那 8 项 —— arena、PTR_TO_FUNC、widening、调用约定、panic 处理——全是 ✅。
他已经在路上了。
原文:LWN.net — BPF in the agentic era
夜雨聆风