eBPF 是目前 Linux 平台比较火的技术,前一段时间 Windows 平台也推出了自己的 eBPF for Windows。eBPF 是 BPF 的扩展,BPF(Berkeley Packate Filter)最初应用在网络包过滤上,相比 netfilter/iptables 具有更高的性能和灵活性,后来随着该项目的发展,越来越多的特性被添加到 BPF 中,目前它除了应用在网络包处理,还可以应用在网络性能分析、安全领域、防火墙、内核和应用调试、设备驱动等等。从层出不穷的应用来看,eBPF 技术还有无限的前景。
eBPF 技术的英文文档非常完备,特别推荐 eBPF 项目文档, tutorial 则推荐 Brendan Gregg 的博客 Learn eBPF Tracing: Tutorial and Examples,非常有趣。eBPF 文档相关内容就不再赘述,我结合这一两周使用 eBPF 的感受,简单的分享一下我的理解。
Brendan Gregg 的比喻非常形象:
eBPF does to Linux what JavaScript does to HTML. (Sort of.)
eBPF 之于 Linux 就像 JavaScript 之于 HTML。
在 JavaScript 出现之前,互联网主要是静态的 “文档”,JavaScript 赋予了 HTML 相当的可编程性,是的网页变得动态可交互,直至今日,甚至可以在网页内运行复杂的游戏、操作系统。而 eBPF 彻底改变了 Linux 内核对一般用户(应用程序)的可编程性。
Linux 内核运行在 Linux 系统最靠近硬件的位置,对系统拥有最高权限,但是出于安全考虑,应用通常只允许通过系统调用与内核交互,这其中会产生相当的性能损耗;如果想突破这一层限制,以往只能编写内核模块,或者修改内核,以便将一些业务下沉到内核态,提升性能;在某些业务场景下,需要观测内核的内部状态,但很可惜,内核的安全性考虑,使得它的内部机理对应用来说是个黑盒。
由于内核在操作系统中的核心地位,对内核不恰当的修改或者内核的异常所带来的后果,要远超过用户态应用,这也限制了开发者深入内核,充分挖掘内核潜力。
eBPF 提供了一种全新的思路,hook 所有的系统调用。
eBPF 由一系列内核调用事件驱动,当发生特定的内核调用时,则对应的 hook callback 被调用。从实现上看,相当于在系统调用函数前、后注入了一些额外的代码,这样我们就可以在进入系统调用之前、后做一些事,比如查看系统调用参数实现监控之类。
需要注意的是,eBPF 工作在内核态,且与内核深度耦合,即使 eBPF 隔离了一些内核实现的复杂性,但 eBPF 在系统调用中注入额外的代码,依然有可能引入致命错误,使内核崩溃。
为了解决这些问题,BPF 做了以下工作:
- eBPF 限制注入代码的写权限,外部注入代码只能终止系统调用,但不能修改系统调用的参数,也就是只能看,不能改。
- eBPF 在将代码注入内核调用前,其自身有一套分析机制,检测恶意代码、异常代码等,避免向内核崩溃和安全问题。
- eBPF 对外部注入代码的循环、尾调用都有限制,避免外部注入代码陷入无限循环,影响系统调用。
等等。
eBPF 注入系统调用的代码是什么形式呢?不同于内核模块的方式,eBPF 实现了一套虚拟机,和与之对应的编译器 BCC,可以将 C (也支持其他一些语言)编写的 eBPF 代码编译为 VM 的 bytecode,这些 bytecode 经过安全验证后,就会被注入制定的系统调用,每当发生这个系统调用时,这些 bytecode 就会在 VM 中执行。
eBPF 采用 VM 实现,我认为有 2 个优势:
- 首先就是安全性,注入代码完全运行在 VM 的沙盒中,不会对系统造成致命危害;
- 其次 bytecode 的装载和卸载非常容易,由 VM 负责即可,无需侵入内核源码。
实际使用下来,如果对内核比较熟悉,借助 eBPF 内核将变得完全透明,几乎可以看到任何内核函数的输入、输出等行为,而且非常安全,对内核性能的影响也非常小。
上图来自 Brendan Gregg 的博客,基于 eBPF 已经有一系列功能强大的工具,几乎所有内核单元都有涉及(包括硬件监控)。更有意思的是,这些工具的实现大多并不复杂,可能百十行代码而已。 bpftrace One-Liners Tutorial 列出了一些 “仅此一行” 但功能炫酷的实现,可以看看。
目前看起来,eBPF 似乎更多的是用于监控,比如网络流量、内核性能等等,但实际上不止于此,之前看到过一个 Fuse with eBPF 的方案,使得 FUSE 的性能损失从 80% 降低到 5%,非常瞩目的成绩。下面是该项目的数据截图,这里是原始文档。
eBPF 是一项令人振奋的技术,随后一段时间,我会保持更新该技术的分享,谢谢大家。