首页 > 其他分享 >eBPF 运行原理和流程

eBPF 运行原理和流程

时间:2024-12-04 22:12:10浏览次数:8  
标签:bpf code BPF 流程 eBPF REG 原理 reg

前言

极客时间 eBPF 核心技术与实战 的学习笔记.
本章说一下 ebpf 的运行原理, 本章有些内容是直接 copy 自课程原文

eBPF虚拟机(执行器)包含了什么

官方的说话, eBPF 是运行在 eBPF 虚拟机中, 而不是直接作用于系统.
但是也有人说, eBPF执行系统更应该称之为 执行器, 因为他并不如 虚拟机, 他们的差异如下:

  • eBPF只提供了有限的指令集, 并不像虚拟机一样是一个完整的系统, 提供了完整的指令集, 这是因为 eBPF 不能影响到系统的稳定性, 所以只提供了一些处理过的指令集.
    在前一章中, 我们可以发现内核态代码部分, 使用 C 直接调用了辅助函数, 这是 eBPF 为了提高开发效率有意为之的.
    那么我们看一下 eBPF 虚拟机包含了哪些部分

eBPF辅助函数

就像上一章的 bpf_get_current_pid_tgid() , eBPF为我们提供了许多辅助函数, 通过这些辅助函数来调用到系统的若干运行信息
这些函数实际上是帮我们调用了内核的其他模块, 但是需要注意的是, eBPF提供的辅助函数并不是全部可用的, 能够调用的函数由 eBPF 的程序类型决定

eBPF验证器

用于确保 eBPF 代码的安全, 验证器会将需要执行的指令创建成有向无环图, 确保执行的指令都是可达的, 再模拟执行指令, 确保指令不是无效的

存储模块

11 个 64 位寄存器、一个程序计数器和一个 512 字节的栈组成的存储模块. 这里控制eBPF程序的执行. 这样的设计, 导致了程序的若干限制:

  • 函数的调用只能有一个返回值
  • 函数调用参数不能超过5个
  • 栈存储不能超过512字节

即时编译器

将 eBPF 程序编译成字节码执行

BPF 映射(map)

大块存储, 可以让用户态程序访问, 来进行数据的读取

BPF指令长什么样

需要先安装 bpftool

apt install bpftool

然后运行

bpftool prog list

会输出当前运行的 bpf 程序, 打印类似于

root@VM-4-12-debian:~# bpftool prog list
3: cgroup_device  name sd_devices  tag 3650d9673c54ce30  gpl
        loaded_at 2024-11-30T14:06:29+0800  uid 0
        xlated 504B  jited 310B  memlock 4096B

其中, 3 是这个 eBPF 程序的编号, cgroup_device 是这个程序的类型, sd_devices 是这个 程序的名字
我们可以再开一个命令行, 运行上一章的 helloworld.py, 在运行时再次运行 bpftool prog list

42: kprobe  name hello_world  tag 38dd440716c4900f  gpl
           loaded_at 2024-12-04T21:12:45+0800  uid 0
           xlated 104B  jited 71B  memlock 4096B
           btf_id 85

发现这次多了一条, 名称就是我们定义的 hello_world, 而我们的程序类型是 kprobe, 编号是 42, 知道了 编号 后, 我们可以查看这个程序的详细指令(42注意替换为你的 编号)

root@VM-4-12-debian:~# sudo bpftool prog dump xlated id 42
int hello_world(void * ctx):
; int hello_world(void *ctx)
   0: (b7) r1 = 33
; ({ char _fmt[] = "Hello, World!"; bpf_trace_printk_(_fmt, sizeof(_fmt)); });
   1: (6b) *(u16 *)(r10 -4) = r1
   2: (b7) r1 = 1684828783
   3: (63) *(u32 *)(r10 -8) = r1
   4: (18) r1 = 0x57202c6f6c6c6548
   6: (7b) *(u64 *)(r10 -16) = r1
   7: (bf) r1 = r10
; 
   8: (07) r1 += -16
; ({ char _fmt[] = "Hello, World!"; bpf_trace_printk_(_fmt, sizeof(_fmt)); });
   9: (b7) r2 = 14
  10: (85) call bpf_trace_printk#-61424
; return 0;
  11: (b7) r0 = 0
  12: (95) exit

其中, ; 开头的行是我们编写的代码, 其他行是转换的指令
就拿 0: (b7) r1 = 33 举例, 0 是指令的行数, (b7) 是十六进制值, 代表BPF 指令码, 具体的码和含义可以参考 bpf-docs/eBPF.md at master · iovisor/bpf-docs , 这里的 b7 代表 64位寄存器赋值, r1 = 33 则是BPF 指令的伪代码
所以上面的详细指令可以翻译成:

  • 第 0-8 行,借助 R10 寄存器从栈中把字符串 “Hello, World!” 读出来,并放入 R1 寄存器中
  • 第 9 行,向 R2 寄存器写入字符串的长度 14(即代码注释里面的 sizeof(_fmt) )
  • 第 10 行,调用 BPF 辅助函数 bpf_trace_printk 输出字符串
  • 第 11 行,向 R0 寄存器写入 0,表示程序的返回值是 0
  • 最后一行,程序执行成功退出
    这些指令先通过 R1 和 R2 寄存器设置了 bpf_trace_printk 的参数, 然后调用 bpf_trace_printk 函数输出字符串, 最后再通过 R0 寄存器返回成功.
    而BPF 虚拟机在接受到这些指令后, 经过校验, 会通过即时编译器模块编译成本地机器指令执行.
    使用命令查看编译后的本地机器指令
bpftool prog dump jited id 42

如果报错说不支持, 是因为内核默认不开启查看机器指令的功能, 可自行搜索解决办法

BPF程序什么时候执行

需要安装模块 strace

apt install strace

使用 strace 工具来查看 hello.py 的运行过程

# -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 函数, 传入了 3 个参数, 实际上 bpf 函数只需要3个参数, 那么这里,这三个参数的含义是:

  • 第一个参数是 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 表示属性的大小.

而在第一章中, 我们就说了, eBPF程序并不是一直运行, 而是指定的事件发生后才触发执行.
我们的 hello.py 中代码写明了调用了 attach_kprobe 进行事件的注册

b.attach_kprobe(event="do_sys_openat2", fn_name="hello_world")

为了验证这个结果, 我们使用 strace 再次获取一下, 这次获取全部的流程而不是只是 ebpf

strace -v -f ./hello.py

会发现调用如下

...
/* 1) 加载BPF程序 */
bpf(BPF_PROG_LOAD,...) = 4
...

/* 2)查询事件类型 */
openat(AT_FDCWD, "/sys/bus/event_source/devices/kprobe/type", O_RDONLY) = 5
read(5, "6\n", 4096)                    = 2
close(5)                                = 0
...

/* 3)创建性能监控事件 */
perf_event_open(
    {
        type=0x6 /* PERF_TYPE_??? */,
        size=PERF_ATTR_SIZE_VER7,
        ...
        wakeup_events=1,
        config1=0x7f275d195c50,
        ...
    },
    -1,
    0,
    -1,
    PERF_FLAG_FD_CLOEXEC) = 5

/* 4)绑定BPF到kprobe事件 */
ioctl(5, PERF_EVENT_IOC_SET_BPF, 4)     = 0
...

所以, 其实eBPF 的程序执行分为如下几步:

  1. 借助 bpf 系统调用,加载 BPF 程序,并记住返回的文件描述符
  2. 查询 kprobe 类型的事件编号。BCC 实际上是通过 /sys/bus/event_source/devices/kprobe/type 来查询的
  3. 调用 perf_event_open 创建性能监控事件。比如,事件类型(type 是上一步查询到的 6)、事件的参数( config1 包含了内核函数 do_sys_openat2 )等
  4. 通过 ioctl 的 PERF_EVENT_IOC_SET_BPF 命令,将 BPF 程序绑定到性能监控事件。

标签:bpf,code,BPF,流程,eBPF,REG,原理,reg
From: https://www.cnblogs.com/chnmig/p/18587322

相关文章

  • 4.4 可靠传输的工作原理
    欢迎大家订阅【计算机网络】学习专栏,开启你的计算机网络学习之旅!文章目录前言1停止等待协议1.1无差错情况1.2出现差错的情况1.3解决方案:超时重传1.4确认丢失与确认迟到1.5信道利用率的瓶颈2连续ARQ协议3Go-Back-N协议前言在现代网络通信中,确保数据......
  • 开发中使用UML的流程_08 PIM-4:定义操作及方法
    目录1、序列图概述2、序列图调用方式3、创建消息与销毁消息4、几项建议1、序列图概述在PIM-4中,系统分析员可以用序列图来表达,系统内部一群对象合力完成某一个系统用例时,执行期间的交互情形。之后,序列图可能通过设计师之手,进行调整,并且成为程序员最关切的设计图之二(另一......
  • Nodejs Express.js 快速入门(详细流程)
    一、简介安装nodejs,推荐使用nvm管理安装。二、创建项目(方式一:空项目手动创建)新建项目文件夹express-demo,cd进入文件夹后,使用命令创建package.json文件:#进入项目文件夹执行下面初始化命令,二选一即可,反正创建了可以后期调整的$cdexpress-demo#方式一:需要......
  • Fiddler 抓包工具:安装及汉化流程教程
    前言现在网络这么发达,数据在网上飞来飞去。不管是搞软件开发的人,想让自己做的软件在网上跑得更顺;还是搞网络安全的,要检查有没有坏蛋在网上搞破坏;又或者只是普通老百姓,好奇手机、电脑上网的时候到底在和网络那边“说”啥。这时候,就得有个厉害的抓包工具来帮忙。Fiddler就是这......
  • rcu的实例、注意事项及原理讲解
    一、背景在之前的内核模块里获取当前进程和父进程的cmdline的方法及注意事项,涉及父子进程管理,和rcu的初步介绍-CSDN博客里我们讲到了如何在rcu锁保护的情况下获取一个进程的父进程的pid和comm,另外也贴了一张浓缩了rcu相关概念精华的整理的思维导图。这篇博客里,我们先不涉及rcu......
  • B站朝夕教育 【.NET9.0+WPF实战三类流程化业务逻辑控制】学习记录 【七】
    播放地址:20241120-.NET9.0+WPF实战三类流程化业务逻辑控制-10_哔哩哔哩_bilibili第16-19节调整代码让拖拽到控制流程图里的模块可以再次拖拽移动MainView.xaml文件主要调整ItemsControl中的节点增加几个事件,这里注意 TargetObject="{BindingRelativeSource={RelativeSource......
  • 【词向量表示】Word2Vec原理及实现
    目录Word2VecHowachieveLookuptableCodingPre-dataingModelNegativesamepleWord2Vec单词与单词之间的向量往往不在同一个向量空间,例如,传统的编码方式:one-hot编码,不同单词[1,0,0]和[0,1,0]之间的余弦相似度为0。因此,Word2Vec希望能够通过训练得到一个新的词向量表达方式,......
  • 面试官:来谈谈Vue3的provide和inject实现多级传递的原理
    前言没有看过provide和inject函数源码的小伙伴可能觉得他们实现数据多级传递非常神秘,其实他的源码非常简单,这篇文章来讲讲provide和inject函数是如何实现数据多级传递的。ps:本文中使用的Vue版本为3.5.13。看个demo先来看个demo,这个是父组件,代码如下:<template><ChildDemo......
  • B站朝夕教育 【.NET9.0+WPF实战三类流程化业务逻辑控制】学习记录 【六】
    播放地址:20241120-.NET9.0+WPF实战三类流程化业务逻辑控制-10_哔哩哔哩_bilibili第14-15节调整代码让拖拽到控制流程图里的模块产生位移NodeModel新增两个属性X,Y记录控件的位置信息1publicabstractclassNodeModel:ObservableObject2{3publicabstra......
  • Python扩展C/C++ 实现原理分析
    Python扩展C/C++实现原理分析https://blog.csdn.net/HaoBBNuanMM/article/details/112243129?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522ab2ac79057d38453c0328d6726560514%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request......