1 什么是eBPF
无需修改内核,也不用加载内核模块,程序员就可以在内核中执行执行自定义的字节码。
eBPF,它的全称是“Extended Berkeley Packet Filter”, 网络数据包过滤模块。我们很熟悉的 tcpdump 工具,它就是利用了 BPF 的技术来抓取 Unix 操作系统节点上的网络包。Linux 系统中也沿用了 BPF 的技术。
借用伯克利实验室的一篇论文 “The BSD Packet Filter: A New Architecture for User-level Packet Capture” 来介绍BPF怎样从内核中抓取数据包
- 内核态对数据包的过滤:内核中实现了一个虚拟机,用户态程序通过系统调用,把数据包过滤代码载入到内核态虚拟机中运行,这样就实现了内核态对数据包的过滤。这一块对应图中灰色的大方块,也就是 BPF 的核心。
- 内核网络协议栈:BPF 模块和网络协议栈代码是相互独立的,BPF 只是通过简单的几个 hook 点,就能从协议栈中抓到数据包。内核网络协议代码变化不影响 BPF 的工作。图中右边的“protocol stack”方块就是指内核网络协议栈。
- 内核中的 BPF filter 模块使用 buffer 与用户态程序进行通讯,把 filter 的结果返回给用户态程序(例如图中的 network monitor),这样就不会产生内核态与用户态的上下文切换(context switch)。
在 BPF 实现的基础上,Linux 在 2014 年内核 3.18 的版本上实现了 eBPF,全名是 Extended BPF,也就是 BPF 的扩展。这个扩展主要做了下面这些改进。
- 对虚拟机做了增强,扩展了寄存器和指令集的定义,提高了虚拟机的性能,并且可以处理更加复杂的程序。
- 增加了 eBPF maps,这是一种存储类型,可以保存状态信息,从一个 BPF 事件的处理函数传递给另一个,或者保存一些统计信息,从内核态传递给用户态程序。
- eBPF 可以处理更多的内核事件,不再只局限在网络事件上。你可以这样来理解,eBPF 的程序可以在更多内核代码 hook 点上注册了,比如 tracepoints、kprobes 等。
eBPF的工作流程:
- 把 eBPF 程序编译成字节码。
- 在载入到 Hook 之前,在虚拟机中对程序进行校验。
- 把程序附加到内核之中,被特定事件触发。
- JIT 编译。
- 在程序被触发时,调用辅助函数处理数据。
- 在用户空间和内核空间之间使用键值对共享数据。
2 wasm-bpf
Wasm-bpf: 为云原生 Webassembly 提供通用的 eBPF 内核可编程能力_开源_郑昱笙_InfoQ精选文章
WebAssembly 已经成为一个用于云原生软件组件的高性能、跨平台和多语言软件沙箱环境,Wasm 轻量级容器也非常适合作为下一代无服务器平台运行时。
2.1 wasm-bpf项目介绍
Wasm-bpf 是一个全新的开源项目,它定义了一套 eBPF 相关系统接口的抽象,并提供了一套对应的开发工具链、库以及通用的 Wasm + eBPF 运行时平台实例,让任意 Wasm 虚拟机或者 Wasm 轻量级容器中的应用,有能力将使用场景下沉和拓展到内核态,获取内核态和用户态的几乎所有数据,在网络、安全等多个方面实现对整个操作系统层面的可编程控制,从而极大的拓展 WebAssembly 生态在非浏览器端的应用场景。
因为无法依赖浏览器中现有可用的 JavaScript 引擎接口,所以目前大多数在浏览器外运行的 Wasm 轻量级容器需要使用 WASI(WebAssembly 系统接口)。这些运行时允许 Wasm 应用程序以与 POSIX 类似(但不完全相同)的方式与其 host 操作系统交互。
但是,相对于传统的容器中可以使用几乎所有的系统调用,目前 WASI 所能提供的系统资源非常有限,目前仅仅在文件系统、socket 网络连接等方面提供了一些基本的支持。对于操作系统底层资源的访问、控制和管理能力仍然存在大量空白,例如对 Wasm 模块或者外部其他进程的执行资源限制与行为观测,对网络包的快速转发和处理,甚至和 wasm 沙箱外的其他进程进行通信,访问外设等,都没有一个比较成熟的解决方案。这也使得大多数的 Wasm 轻量级容器在实际应用中还是主要集中于纯粹的计算密集型应用,而在网络、安全等方面,还是需要依赖于传统的容器技术。
类似于浏览器中运行的 Wasm 程序,通过 JavaScript 引擎接口访问浏览器提供的各种系统资源,Wasm-bpf 的方案就是借助 eBPF 虚拟机访问操作系统的各类资源;得益于 eBPF 目前在 Linux 内核甚至 Windows 等其他操作系统中的广泛支持,以及不同内核版本和架构之间的可移植性,和内核 BPF 验证引擎的可靠性,我们仍然可以在一定程度上保证应用的可移植性和安全边界。
2.2 wasm-bpf项目进展
Wasm-bpf 项目已经实现了内核态 eBPF 虚拟机和用户态之间系统接口完整的抽象机制,并提供了对应的工具链以将 eBPF 应用编译为 Wasm 模块,帮助进行内核态 eBPF 和用户态 Wasm 之间无序列化,共享内存的高效双向通信,并通过代码生成技术,提供和其他用户态 eBPF 开发框架几乎一致的、简单便捷的开发体验。借助 Wasm 组件模型不断完善的生态支持,我们也可以为 eBPF 社区带来更多用户态开发语言,不同语言实现的可观测性、网络等 eBPF 应用和数据处理插件也可以被轻松集成、复用、统一管理。
在几乎已经成为 eBPF 用户态事实上的 API 标准的 libbpf 库,和 WAMR(wasm-micro-runtime) 之上,只需要 300+ 行代码即可构建完整的通用 Wasm-eBPF 运行组件,并支持大多数的 eBPF 使用场景 -- 任何人用任何主流 Wasm 运行时,或者任何 eBPF 用户态库,以及任何编程语言,都可以轻松添加对应的虚拟机支持,并复用我们的工具链轻松实现 Wasm-eBPF 程序的编写和开发。
之前在 eunomia-bpf 项目中,已经有一些将 eBPF 和 Wasm 结合的探索,但它并不是为了 Wasm 原生应用的场景设计的,不符合 Wasm-eBPF 的通用编程模型,性能也较为低下,因此我们创建了一个新的开源仓库,让 Wasm-bpf 项目专注于利用 eBPF 增强和扩展 WebAssembly 使用场景,并进一步完善对应的工具链和开发库支持:GitHub - eunomia-bpf/wasm-bpf: WebAssembly library, toolchain and runtime for eBPF programs
2.3 eBPF能给wasm带来什么
eBPF 是一项革命性的技术,起源于 Linux 内核,可以在操作系统的内核中运行沙盒程序。它被用来安全和有效地扩展内核的功能,而不需要改变内核的源代码或加载内核模块。
从历史上看,由于内核具有监督和控制整个系统的特权能力,所以操作系统一直是实现可观察性、安全性和网络功能等多种能力的理想场所。同时,由于操作系统内核对稳定性和安全性的高要求,内核的新功能迭代通常非常谨慎,也很难接受自定义的、较少通用性的功能改进。因此,与用户态的更多功能相比,内核态操作系统层面的创新率历来都比较低。
OpenGL:主要做的事情是提供统一的接口调用显卡驱动,让我们屏蔽了不同的显卡——只要按OpenGL教程介绍的方式去渲染一个三角形,不管你在什么操作系统,使用什么显卡,都能正确渲染出来(大概率吧)。