1.1.3 eBPF与cBPF的功能区别

BPF指令集是一个通用的RISC指令集,指令集由指令操作码和寄存器组成。1992年诞生了BPF技术,当时的寄存器和指令数目非常有限,到后来eBPF技术发展起来,寄存器和指令数目多了很多。为了区别,原来的BPF又被称为cBPF(classic BPF,经典BPF)。

下面从功能上对比一下cBPF和eBPF。

1)cBPF支持的功能比较单一,比如常用于网络数据包过滤的tcpdump。而eBPF除了能够支持网络的数据包过滤外,也支持其他的事件类型,如XDP、Perf Event、kprobe、tracepoint等。

2)e B P F支持JIT编译生成目标机器码,运行效率高。而cBPF只能解释执行,性能差。

3)eBPF引入map机制:cBPF需要通过接收队列获取过滤后的数据,但是eBPF可以将数据放到map空间中。map空间是用户空间和内核空间共享的,所以一般是先在内核将数据存入map空间,然后在用户空间取出数据。或者在用户空间写入一些控制逻辑,内核空间根据这些逻辑进行分支选择。

4)因为要支持更多功能,所以eBPF指令集变得更复杂了。与此同时,有了专门编译BPF字节码的编译器Clang/LLVM,这样就可以基于C语言等进行BPF程序的开发,而不是直接写BPF汇编。

5)eBPF还在安全机制等方面有一些改变。

cBPF包含16个32位的临时寄存器,其索引编号为0~15。每条cBPF汇编指令为64位,其中16位的code字段表示具体的操作类型,包括加载/存储、跳转和运算等类型。另外,8位的jt和jf字段用于指定代码的跳转偏移量,其中jt用于真跳转,jf用于假跳转。最后,32位的k字段为通用值,在不同的指令类型下具有不同的含义。表1-2是cBPF寄存器的分类。

表1-2 cBPF寄存器的分类

eBPF由11个64位寄存器、一个程序计数器(PC)和一个512字节的大BPF堆栈空间组成。可使用的寄存器被命名为r0~r10。eBPF操作数位宽默认为64位。64位的寄存器也可作为32位子寄存器使用,它们只能通过特殊的ALU(算术逻辑单元)操作访问,使用其中的低32位,剩余的高32位用零填充。表1-3是eBPF寄存器的分类。

表1-3 eBPF寄存器的分类