乐趣区

关于linux:eBPF的发展演进从石器时代到成为神一

  1. 前言
    技术的倒退往往是积跬步而至千里的。Linux 从 92 年诞生,倒退至今曾经笼罩大小各类的信息基础设施。是什么样的力量,让 Linux 可能始终保持倒退生机,又如何对待 Linux 之上呈现的新的技术趋势?

本文试图通过梳理 eBPF 的演进过程,摸索 Linux 内核的倒退能源起源与倒退轨迹,与大家一起畅想 eBPF 给内核技术、Linux 生态带来的全新变局。

  1. eBPF 概览
    2.1. 实现原理

大家可能都晓得图灵机,这是一个可计算实践模型,能够用来判断计算机的计算能力。图灵机是目前有可能实现的计算能力最强的实践模型,目前咱们罕用的计算机,实践上都是等价于图灵机的。
BPF 的呈现,是对计算能力的渴求,其原理就是通过 IR 模仿一台 RISC 指令集的计算机嵌入到内核中,将内核外部的动态编译逻辑转变为更加灵便的动静编译逻辑,使内核取得近似于图灵机的动静逻辑定制能力。而从 classic BPF 到 extended BPF 的倒退,是将这一计算形式进一步夯实和通用化。

BPF 的呈现乃至到 eBPF 的进一步倒退,为内核带来了微小的扭转,使内核具备了更加弱小、可编程的动态变化的能力。这种能力在各种须要定制化的利用场景中,将施展微小的价值,既能够用于扩大性能,也能够用于优化性能。

在实现上,为适应不同业务场景的需要,使 eBPF 具备等价于一台 RISC 指令集计算机的计算能力,通过输出参数、Map 数据存储、Helper 帮忙函数,形成了 eBPF 程序与内核交互的运行环境。eBPF 指令集的计算和控制能力、运行环境与内核的交互能力,两者叠加形成了 eBPF 程序弱小的解决能力。

在平安方面,通过 Verifier 严格查看 eBPF 程序的可实现性、数据拜访的合法性等,保障了 eBPF 程序与内核交互过程中内核不被挂起、外围数据不会被毁坏。

BPF 倒退过程中,由 cBPF 倒退成为 eBPF 是一次大的技术升级。eBPF 在 cBPF 的根底上从新设计了指令集、引入了 JIT、减少了辅助函数,大大扩大了简单逻辑的设计能力。尽管 eBPF 有微小的提高,然而根本的底层设计还是统一的,因而两者统称为 BPF。

因为 eBPF 兼容 cBPF,在未指定时,BPF 更多指 eBPF 所定义的外延。后文用 BPF 泛指整个 BPF 相干的根底机制,eBPF 特指最新的 BPF 规范。

2.2. 技术特点

BPF 还在疾速倒退,它的计算能力和齐备性也在迅速进步,前景有限。但就具体的版本而言,却又出现具体技术特点,次要是其反对的能力和受到的束缚两个方面。以最新的 BPF 的技术标准 (v6.1) 为底本,介绍 BPF 的次要技术特点。

RISC 指令集

BPF 的外围是一个虚构计算机,它采纳类 RISC 指令集,反对跳转、算数运算、尾调用等基本操作。在运行 BPF 程序的计算机上,BPF 指令会被内核的 JIT 编译器动静编译为物理机原生指令,实现运行效率的“零”损耗。在反对 BPF 卸载的设施上,BPF 程序也能够卸载到设施上执行。在 BPF 的指令集中还反对伪调用指令,能够调用到内核帮忙函数。

同时,BPF 的指令的编码空间中还有大量的储备,将来依据须要肯定还会持续减少指令,晋升 BPF 实现简单逻辑的能力。

Map
基于键值对的数据存储机制,可用于实现内核、用户态的数据存储和替换。

Helper 函数
专用于 BPF 程序调用的函数接口,用于封装内核中的性能,使 BPF 程序能够和内核互操作,同时放弃 BPF 程序和内核的平安隔离。

BPF 子程序
实现了 BPF 程序之间的调用。

上下文
BPF 程序的语境和运行上下文,是一种外部通明的数据结构。只有在明确 BPF 程序的类型时,上下文的定义和外部数据结构才是确定的。不同的 BPF 程序类型,上下文也各不相同。

CO-RE
通过运行时类型反对,实现一次编译、随处运行。

反对特权和非特权级两类运行模式

分为特权级(百万 ins)和非特权级(4096ins)两类运行形式。

特权级模式下 BPF 程序能够取得更宽的权限,实现更简单的逻辑性能。

保障向后兼容

这一准则对于 BPF 的推广应用十分重要,能够保障旧规范的 BPF 程序在新规范下也能够正确执行。但同时,也对将来 BPF 倒退带来了束缚,只有把握好 BPF 的倒退方向,做好底层设计,能力两者失去兼顾。

比方,从老版本遗留下来的 cBPF 程序在 eBPF 中都会被 JIT 正确翻译和执行。

稳固的 ABI

BPF 稳固的 ABI 包含,BPF 程序类型对应的输出参数定义,可调用的内核帮忙函数定义,返回值定义等。应用稳固的 ABI 的 BPF 程序,可保障与不同版本的内核都是兼容的。

另外,BPF 还在疾速倒退中,它的性能个性须要逐渐开释,因而目前还有诸多限度,其中有些是基于平安、可靠性思考,有些是没有超出范围的利用需要的激进设计等等。随着平安机制的欠缺、应用程序的扩大、生态体系的成熟,相应的限度也会逐渐的扭转。

目前的实现中,有如下限度:

总运行工夫有界

有界性这是根本准则,应该在比拟长的工夫内都不会扭转。然而,在不扭转有界性的前提下,依据具体须要适当调整更正当的下限,这是存在极大可能的。

指令总数限度

非特权用户最大指令数 4096,特权用户最大指令数 1 百万。

分支数限度
BPF 调用嵌套档次限度
Map 实例数限度
验证状态数限度
最大分支数限度
堆栈长度限度

目前反对的堆栈最大长度为 512 字节。

上下文限度
每一种类型的 BPF 程序,都有其对应输出参数定义,彼此不同。也就是说,BPF 程序只能承受特定的输出并进行解决,不能拜访内核的全副状态空间。

辅助函数限度

每一个 BPF 程序类,都有其对应的辅助函数汇合。这些辅助函数,由内核各子系统提供,是 BPF 程序类上下文的一部分。它们帮忙 BPF 程序与内核各子系统交互,同时又爱护内核不会被毁坏。

下面赘述了很多个性,大家可能会有很多疑难,比方:

为什么采纳精简指令集呢?因为这是目前最支流的指令集类型,绝对于简单指令集,精简指令集更有利于实现更高密度、更高吞吐量、更高主频的处理器。因而 x86 之后呈现的新型指令集零碎,绝大多数都是精简指令集,包含当初的开源指令集 RISC-V。

为什么不采纳原生的指令集呢?
为什么 5 个参数寄存器呢?
本篇暂不深刻探讨,后续主题波及到的时候再具体解说。

2.3. 利用价值

BPF 的利用价值与其动静和可定制个性强相干。

内核研发中始终坚守的准则是:“机制与策略拆散”,即:内核负责提供机制,将策略凋谢给下层。在机制与策略之间须要一层界面来进行交互。

零碎调用是最后计划。它是单向发动的,短少事件模型。

虚构文件系统,提供了双向的交互方式,但难以灵便定制简单的逻辑。

因为软件性能越来越简单,无奈用简略规定来表白,软件的根底性能设施与业务逻辑,须要进行解偶。而业务逻辑局部,须要依据业务定制,因而很适宜用 BPF 实现。比方:

过滤器
权限查看
含糊测试

等类型的性能,比拟适宜用 BPF 实现。另外,视具体问题,也能够利用于:

调度算法
用户态交互(代替零碎调用,实现更加可变的服务逻辑)
加载器、模拟器、兼容层
轻量化内核
多态内核
启动形式

每一种业务类型都有其独具特色的逻辑模型,通过更形式化地定义这些业务模型,能够更好地了解它们和 BPF 的联合性,找到更好的实现计划,充分发挥 BPF 带来的弱小能力。后续篇章,咱们会对典型的利用模型进行更深刻的探讨,以及 BPF 在这些利用场景中,应该在哪些个性方面进行增强或改良。

退出移动版