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在这些利用场景中,应该在哪些个性方面进行增强或改良。