eBPF 虚拟机是如何工作的

简介: 【2月更文挑战第3天】

eBPF 是一个运行在内核中的虚拟机,很多人在初次接触它时,会把它跟系统虚拟化(比如 kvm)中的虚拟机弄混。其实,虽然都被称为“虚拟机”,系统虚拟化和 eBPF 虚拟机还是有着本质不同的。


系统虚拟化基于 x86 或 arm64 等通用指令集,这些指令集足以完成完整计算机的所有功能。而为了确保在内核中安全地执行,eBPF 只提供了非常有限的指令集。这些指令集可用于完成一部分内核的功能,但却远不足以模拟完整的计算机。为了更高效地与内核进行交互,eBPF 指令还有意采用了 C 调用约定,其提供的辅助函数可以在 C 语言中直接调用,极大地方便了 eBPF 程序的开发。


eBPF 在内核中的运行时主要由  5  个模块组成:

image.png

  • 第一个模块是  eBPF 辅助函数。它提供了一系列用于 eBPF 程序与内核其他模块进行交互的函数。这些函数并不是任意一个 eBPF 程序都可以调用的,具体可用的函数集由 BPF 程序类型决定。
  • 第二个模块是  eBPF 验证器。它用于确保 eBPF 程序的安全。验证器会将待执行的指令创建为一个有向无环图(DAG),确保程序中不包含不可达指令;接着再模拟指令的执行过程,确保不会执行无效指令。
  • 第三个模块是由  11 个 64 位寄存器、一个程序计数器和一个 512 字节的栈组成的存储模块。这个模块用于控制 eBPF 程序的执行。其中,R0 寄存器用于存储函数调用和 eBPF 程序的返回值,这意味着函数调用最多只能有一个返回值;R1-R5 寄存器用于函数调用的参数,因此函数调用的参数最多不能超过 5 个;而 R10 则是一个只读寄存器,用于从栈中读取数据。
  • 第四个模块是即时编译器,它将 eBPF 字节码编译成本地机器指令,以便更高效地在内核中执行。
  • 第五个模块是  BPF 映射(map),它用于提供大块的存储。这些存储可被用户空间程序用来进行访问,进而控制 eBPF 程序的运行状态。


eBPF 程序是什么时候执行的,查看到 BCC 的执行过程,首先,打开一个终端,执行下面的命令:

# -ebpf表示只跟踪bpf系统调用
sudo strace -v -f -ebpf ./hello.py

稍等一会,会看到如下的输出:

bpf(BPF_PROG_LOAD,
    {
        prog_type=BPF_PROG_TYPE_KPROBE,
        insn_cnt=13,
        insns=[
            {code=BPF_ALU64|BPF_K|BPF_MOV, dst_reg=BPF_REG_1, src_reg=BPF_REG_0, off=0, imm=0x21},
            {code=BPF_STX|BPF_H|BPF_MEM, dst_reg=BPF_REG_10, src_reg=BPF_REG_1, off=-4, imm=0},
            {code=BPF_ALU64|BPF_K|BPF_MOV, dst_reg=BPF_REG_1, src_reg=BPF_REG_0, off=0, imm=0x646c726f},
            {code=BPF_STX|BPF_W|BPF_MEM, dst_reg=BPF_REG_10, src_reg=BPF_REG_1, off=-8, imm=0},
            {code=BPF_LD|BPF_DW|BPF_IMM, dst_reg=BPF_REG_1, src_reg=BPF_REG_0, off=0, imm=0x6c6c6548},
            {code=BPF_LD|BPF_W|BPF_IMM, dst_reg=BPF_REG_0, src_reg=BPF_REG_0, off=0, imm=0x57202c6f},
            {code=BPF_STX|BPF_DW|BPF_MEM, dst_reg=BPF_REG_10, src_reg=BPF_REG_1, off=-16, imm=0},
            {code=BPF_ALU64|BPF_X|BPF_MOV, dst_reg=BPF_REG_1, src_reg=BPF_REG_10, off=0, imm=0},
            {code=BPF_ALU64|BPF_K|BPF_ADD, dst_reg=BPF_REG_1, src_reg=BPF_REG_0, off=0, imm=0xfffffff0},
            {code=BPF_ALU64|BPF_K|BPF_MOV, dst_reg=BPF_REG_2, src_reg=BPF_REG_0, off=0, imm=0xe},
            {code=BPF_JMP|BPF_K|BPF_CALL, dst_reg=BPF_REG_0, src_reg=BPF_REG_0, off=0, imm=0x6},
            {code=BPF_ALU64|BPF_K|BPF_MOV, dst_reg=BPF_REG_0, src_reg=BPF_REG_0, off=0, imm=0},
            {code=BPF_JMP|BPF_K|BPF_EXIT, dst_reg=BPF_REG_0, src_reg=BPF_REG_0, off=0, imm=0}
        ],
        prog_name="hello_world",
        ...
    },
    128) = 4

这些参数看起来很复杂,但实际上,如果查询  bpf 系统调用的格式(执行  man bpf 命令),就可以发现,它实际上只需要三个参数:

int bpf(int cmd, union bpf_attr *attr, unsigned int size);


对应前面的 strace 输出结果,这三个参数的具体含义如下。


第一个参数是  BPF_PROG_LOAD , 表示加载 BPF 程序。


第二个参数是  bpf_attr  类型的结构体,表示 BPF 程序的属性。其中,有几个需要你留意的参数,比如:

  • prog_type  表示 BPF 程序的类型,这儿是  BPF_PROG_TYPE_KPROBE ,跟我们 Python 代码中的  attach_kprobe  一致;
  • insn_cnt  (instructions count) 表示指令条数;
  • insns  (instructions) 包含了具体的每一条指令,这儿的 13 条指令跟我们前面  bpftool prog dump  的结果是一致的;
  • prog_name  则表示 BPF 程序的名字,即  hello_world 。


第三个参数 128 表示属性的大小。

相关文章
|
9月前
|
大数据 Linux
【大数据环境准备】(一)VM虚拟机环境准备
装机环境安装,准备3台vm虚拟机,内存配置在4c8u 基础配置
128 0
|
测试技术 虚拟化 Windows
虚拟机的运行架构
虚拟机作为应用软件安装在操作系统上 可以在此应用软件上安装多个操作系统 直接安装在硬件上的系统为宿主
108 0
虚拟机的运行架构
|
运维 监控 Java
JVM07-虚拟机故障处理之命令行工具
上一篇我们介绍了JVM06-经典垃圾收集器。这篇文章将介绍用来排查处理虚拟机故障的一些常用的命令行工具。因为如果我们要对JVM进行调优时,必须要通过这些工具分析虚拟机的运行状态。
101 0
JVM07-虚拟机故障处理之命令行工具
|
分布式计算 安全 Hadoop
虚拟机环境准备|学习笔记
快速学习 虚拟机环境准备
|
分布式计算 Oracle 关系型数据库
硬核!!教你如何通过脚本自动部署虚拟机并安装操作系统
硬核!!教你如何通过脚本自动部署虚拟机并安装操作系统
464 0
硬核!!教你如何通过脚本自动部署虚拟机并安装操作系统
|
Linux KVM 虚拟化
|
运维 监控 Java
深入理解虚拟机之虚拟机性能监控和故障处理工具
给一个系统定位问题的时候,知识、经验是关键基础,数据是依据,工具是运用知识处理数据的手段。经常 使用适当的虚拟机监控和分析工具可以加速我们分析数据、定位解决问题的速度,但在学习工具前,也应当意识到工具永远都是知识技能的一层包装,并没有什么工具是“秘密武器”,不可能学会了就能包治百病。
2885 0
|
Java 编译器 自然语言处理
虚拟机优化
本博客为《深入理解java虚拟机》的学习笔记,所以大部分内容来自此书,另外一部分内容来自网络其他博客和源码分析。 主要内容包括:前期(编译期)优化,后期(运行期)优化。   一  前期(编译期)优化 1       编译过程 从javac的角度来看,编译过程大致分为三步: Ø  解析与填充符号表过程。
1703 0