BPF设计问答

BPF在网络,跟踪,Linux内核中的安全性以及BPF虚拟机的几种用户空间实现中的可扩展性和适用性导致对BPF实际上是什么存在许多误解。简短的质量检查旨在解决这一# 问题,并概述BPF长期发展的方向。

问:BPF是类似于x64和arm64的通用指令集吗?

答:不可以。

问:BPF是通用虚拟机吗?

答:不可以。

BPF是具有C调用约定的通用指令集。

问:为什么选择C调用约定?

答:由于BPF程序设计为在用C编写的linux内核中运行,因此BPF定义了与两种最常用的架构x64和arm64兼容的指令集(并考虑了其他架构的重要特性),并定义了调用约定与那些体系结构上的Linux内核的C调用约定兼容。

问:将来可以支持多个返回值吗?

答:不可以。BPF仅允许将寄存器R0用作返回值。

问:将来可以支持5个以上的函数参数吗?

答:不可以。BPF调用约定仅允许将寄存器R1-R5用作参数。BPF不是独立的指令集。(与允许msft,cdecl和其他约定的x64 ISA不同)

问:BPF程序可以访# 问指令指针或返回地址吗?

答:不可以。

问:BPF程序可以访# 问堆栈指针吗?

答:不可以。

仅可访# 问帧指针(寄存器R10)。从编译器的角度来看,必须具有堆栈指针。例如,LLVM将寄存器R11定义为其BPF后端中的堆栈指针,但它确保生成的代码从不使用它。

问:C调用约定是否会减少可能的用例?

答:可以。

BPF设计强制以内核助手功能和内核对象(如BPF映射)的形式添加主要功能,并在它们之间实现无缝互操作性。它允许内核调用BPF程序,并且程序以零开销调用内核帮助程序,因为它们全部都是本机C代码。与本地内核C代码无法区分的JITed BPF程序尤其如此。

问:这是否意味着不允许对BPF代码进行“创新”扩展?

答:是的。

至少到目前为止,直到BPF内核支持bpf到bpf调用,间接调用,循环,全局变量,跳转表,只读段以及C代码可以生成的所有其他常规构造。

问:可以安全地支持循环吗?

答:目前还不清楚。

BPF开发人员正在尝试找到一种方法来支持有界循环。

问:验证者的限制是什么?

答:用户空间已知的唯一限制是BPF_MAXINSNS(4096)。这是无特权的bpf程序可以拥有的最大指令数。验证者有各种内部限制。就像程序分析期间可以探索的最大指令数一样。目前,该上限设置为100万。这实质上意味着最大的程序可以包含100万个NOP指令。后续分支的最大数量受到限制,嵌套bpf到bpf调用的数量受到限制,每条指令的验证器状态数量受到限制,程序使用的映射数量也受到限制。所有这些限制都可以通过足够复杂的程序来实现。还有一些非数字限制可能导致程序被拒绝。验证程序仅用于识别指针+常量表达式。现在,它可以识别指针+ bounded_register。bpf_lookup_map_elem(key)要求’key’必须是指向堆栈的指针。现在,“键”可以是指向映射值的指针。验证程序正在逐渐变得“更智能”。限制已被删除。知道程序将被验证程序接受的唯一方法是尝试加载它。bpf开发过程保证将来的内核版本将接受较早版本接受的所有bpf程序。

指令级问题

问:LD_ABS和LD_IND指令与C代码

问:BPF中如何出现LD_ABS和LD_IND指令,而C代码无法表达它们,而必须使用内置的内在函数?

答:这是与经典BPF兼容的产物。没有它们,BPF中的现代网络代码性能会更好。请参阅“直接数据包访# 问”。

问:BPF指令不是一对一映射到本机CPU

问:似乎并非所有BPF指令都是与本机CPU一对一的。例如,为什么BPF_JNE和其他比较和跳转不像CPU?

答:这是避免将标志引入ISA的必要条件,这些标志不可能在整个CPU体系结构中通用且高效。

问:为什么BPF_DIV指令不映射到x64 div?

答:因为如果我们选择与x64一对一的关系,那么在arm64和其他拱门上进行支撑将变得更加复杂。另外,它还需要按零除运行时检查。

问:为什么没有用于签名除法运算的BPF_SDIV?

答:因为它很少使用。llvm在这种情况下会出错,并显示使用无符号除法的建议。

#问:为什么BPF有隐含的序言和结语?

答:因为sparc之类的体系结构具有注册窗口,并且通常在体系结构之间存在足够细微的差异,所以将幼稚的存储返回地址放入堆栈中将不起作用。另一个原因是BPF必须避免被零除(以及LD_ABS insn的旧式异常路径)。这些指令需要调用结尾并隐式返回。

问:为什么一开始没有引入BPF_JLT和BPF_JLE指令?

答:因为经典的BPF没有它们,而且BPF的作者认为编译器的解决方法是可以接受的。事实证明,由于缺少这些比较指令,程序失去了性能,因此将它们添加了。这两个指令是一个完美的例子,什么样的新BPF指令是可以接受的,并且将来可以添加。这两个在本机CPU中已经具有等效的指令。没有与硬件指令一对一映射的新指令将不被接受。

问:BPF 32位子寄存器要求

问:BPF 32位子寄存器要求BPF寄存器的高32位为零,这使BPF在32位CPU体系结构和32位硬件加速器方面效率低下。将来可以将真正的32位寄存器添加到BPF吗?

答:不可以。

但是,可以对BPF寄存器的高32位置零进行一些优化,并且可以利用这些优化来提高针对32位架构的JITed BPF程序的性能。

从版本7开始,如果传递了选项-mattr = + alu32来编译程序,则LLVM能够生成对32位子寄存器进行操作的指令。此外,验证者现在可以标记需要将目标寄存器的高位清零的指令,并插入显式的零扩展(zext)指令(mov32变体)。这意味着对于没有zext硬件支持的体系结构,JIT后端不需要清除由alu32指令或狭窄负载写入的子寄存器的高位。相反,后端只需要支持该mov32变体的代码生成,并覆盖bpf_jit_needs_zext()以使其返回“ true”(以便在验证程序中启用zext插入)。

请注意,JIT后端可能对zext具有部分硬件支持。在这种情况下,如果启用了验证程序zext插入,则可能导致插入不必要的zext指令。可以通过在JIT后端内部创建一个简单的窥孔来删除此类指令:如果一个指令具有对zext的硬件支持,并且如果下一条指令是显式的zext,则在进行代码生成时可以跳过后者。

问:BPF是否具有稳定的ABI?

答:可以。BPF指令,BPF程序的参数,辅助函数集及其参数,可识别的返回码都是ABI的一部分。但是,跟踪程序有一个特定的例外,该程序使用诸如bpf_probe_read()之类的助手来遍历内核内部数据结构并使用内核内部标头进行编译。这两个内核的内部结构都可能会发生变化,并且可能会与较新的内核一起破坏,因此需要对程序进行相应的调整。

问:BPF程序使用多少堆栈空间?

答:当前所有程序类型都限制为512字节的堆栈空间,但是验证程序会计算实际使用的堆栈数量,并且解释程序和大多数JIT代码都会消耗必要的数量。

问:可以将BPF卸载到硬件吗?

答:可以。NFP驱动程序支持BPF硬件卸载。

问:经典的BPF解释器是否仍然存在?

答:不可以。经典BPF程序将转换为扩展BPF指令。

问:BPF可以调用任意内核函数吗?

答:不可以。BPF程序只能调用为每种程序类型定义的一组辅助函数。

问:BPF可以覆盖任意内核内存吗?

答:不可以。

跟踪bpf程序可以使用bpf_probe_read()和bpf_probe_read_str()帮助器读取任意内存。网络程序无法读取任意内存,因为它们无法访# 问这些帮助程序。程序永远不能直接读取或写入任意内存。

问:BPF可以覆盖任意用户内存吗?

某种。

跟踪BPF程序可以使用bpf_probe_write_user()覆盖当前任务的用户内存。每次加载此类程序时,内核都会打印警告消息,因此该帮助程序仅对实验和原型有用。跟踪BPF程序仅是root用户。

问:通过内核模块有新功能吗?

问:可以从内核模块代码中添加BPF功能,例如新程序或映射类型,新助手等吗?

答:不可以。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值