直播回放总结 | eBPF简介
什么是BPF
它是可以在内核中运行的沙盒程序,这个沙箱程序是可以被一些事件触发的,它的优点就是不需要修改内核的源代码,或者装载内核模块,是可以在用户态自定义它的功能,并且可以保证内核的安全性,属于一种新类型的软件,另外,虽然它是起源于内核,但是bpf在最近两年发展非常快,一个重要原因是它可以帮助我们用户态的应用提高性能,或者说做安全、可观测性等方面工作,并且它的能力非常强大,所以它非常适用于用户态场景,这也是bpf发展这么快的一个原因。
那么到底怎么形容bpf的功能,可以通过javascript做一个对比:我们有js之前,在网页上的是静态的网页,但是我们加入js之后,就可以在网页中实现一些更为复杂功能。比如说网页的实时刷新、在地图上做一个交互,在网页上点一个按钮控制影片播放/暂停等等。通过事件触发js。并且它是运行在沙盒里,具有安全性保证,性能也是由jit支持。而bpf和它这种非常类似,只不过bpf是在操作系统内核干了这件事。有了bpf之后就可以在内核运行时做一些实时性很强的事情,例如信息提取,另外也可在特定的场景下改变内核的一些行为。ebpf的事件不像js是一些硬件事件,而是内核函数,比如说在内核中某一个函数点被执行了,那么他有一个kprobe,或者traceprint这种内核里边的钩子,当内核在执行到这种函数时就会触发这个事件,所以说bpf是靠这些东西来触发的,由于他是在vm中运行,所以也能保证内核安全,另外,他的每一个程序码都需要进入到内核,所以对安全性要求非常高,verfier校验器就是来验证ebpf字节码是否安全,而对于性能,jit编译器则将bpf字节码编译成适合本机架构的机器码运行。
前面说的ebpf是内核事件触发,那么他到底是怎么一个事件触发的呢?其实我们关注的bpf事件就两个:实现的逻辑和数据的存储。数据存储是在bpf内部,因为这部分数据在内核中比较特殊,所以他有一个map结构,他是以k-v键值对的形式存储数据。事件触发对于probe来说,就是创建了一个bpf程序,并且已经load,但是程序不会在cpu上去执行,只有在某些内核事件触发的时候,CPU才会去执行bpf程序,当执行完成之后会被CPU调度走变成等待状态,等待事件被再次触发。经过这两年发展目前,bpf程序也支持锁(spin lock)和sleep状态,ebpf程序的实现逻辑简单来说就是代码,但是它使用的是一组接口稳定、功能受限的help函数。我们在内核中只能使用这些help函数来实现功能逻辑,不会像我们写用户代码或者内核模块那样随意调用函数。
ebpf 程序可由sockets、tracepoints、usat、kprobes、uprobes、perf_events事件驱动,这些都是目前内核里面基础事件,里面一部分是网络,一部分是tracing,tracing是做内核可观测性的,比如kprobe、uprobe,也就是说内核态或者是用户态函数我们都可以做挂载。通过int 3指令陷入到内核中,同时也可以做一些比如数据过滤之类的行为。Bpf正是利用了这些事件,来执行自己的一些代码逻辑,所以说这些事件的功能就很有可能是ebpf具有的功能。而这些功能存在的一些限制也很有可能是ebpf存在的限制。
Linux 系统不需要进行任何操作就已经在运行bpf程序,而要查看这些信息只要使用bpftool工具即可。它可以方便的对bpf程序进行管理、观测。
Bpftool prog show 命令即可查看当前系统运行的bpf程序。
BPF字节码
用户态的bpf程序经过clong、llvm编译后会形成bpf字节码,进入内核后有一个jit编译器将bpf字节码编译成本机架构能够识别的机器码,这样可以保证用户态的bpf程序不会因架构不同导致程序不能运行。
字节码组成:
查看bpf字节码
首先要向内核注入一个bpf程序:
该程序利用kprobe事件将程序挂载到do_sys_open内核函数上,获取进程pid和文件名,并使用bpf_printk函数将信息打印到trace_pipe文件中,通过访问文件内容即可查看打印信息。
运行结果:
Bpftool prog show 命令可以查看新注入的bpf程序:
使用Bpftool prog dump xlated id 40 命令查看新注入bpf程序详细信息
第一个红框中的函数会拿到当前进程pid,而这个函数就是bpf提供的接口稳定但功能受限的API。Bpf就是利用这些函数完成相关程序功能。
查看bpf字节码:
命令:Bpftool prog dump xlated id 40 opcode
内核中对相关字节码有对应定义,比如opcode内容由内核中#define BPF_CALL 0x80 和#define BPF_JMP 0x05 两个宏定义或运算得到,对应红框中第一个85字节码。80 10对应bpf_get_current_pid_tgid()函数字节码,在bpf_helper_defs.h文件中对该函数有如下定义:
经过检验器偏移计算即可得到对应字节码。
Bpf 进入内核入口:BPF Syscall
虽然bpf观测内核有别于内核模块,但是Bpf程序依旧逃不过利用系统调用进入内核,我们可以使用strace工具来追踪bpf程序调用的整个过程,内核对bpf系统调用定义如下:
从中可以看出代码中使用了大量的switch、case语句,而相关cmd在内核中定义如下:
其中BPF_MAP_CREATE表示bpf程序中有map结构,那么就会调用map_create()函数。
Verifier、jit
Verifier 、jit都发生在bpf_prog_load函数中,对应cmd=BPF_PROG_LOAD,这个函数主要功能如下:
1、 校验license
2、 判断bpf指令数目
3、 判断权限
4、 把bpf字节码从用户态拷贝到内核中
5、 Bpf_check() 进行verifier校验
6、 Bpf_prog_select_runtime() 进行jit编译
7、 分配id和文件句柄
Verifier
函数入口在bpf_check(),主要做如下事情:
1、 使用DAG的DFS算法对bpf程序的每一个字节码、每一条代码路径进行程序校验
2、 严格限制bpf程序对内存访问
3、 参数校验
4、 仅允许有限循环
5、 修改字节码,把函数编号替换成指针
JIT
入口:do_jit(),jit会将bpf字节码解析编译成本机能够允许的字节码,所以它和硬件的具体架构相关。若程序不支持JIT时,使用interpreter内核解析器解析程序。
一些bpf框架/脚手架/工具
现有的基于bpf开发的性能工具:
BCC ,bpftrace
基于bpf开发的复杂功能,产品:
Libbpf c (CO-RE),BCC python,cilium/ebpf Go、libbpf-rs
快速验证单个功能模型:
BCC
查看bpf信息,例如字节码、挂载点、prog、map等信息:
Bpftool(CLI tool) 、bpftrace
视频链接:
http://www.bilibili.com/video/BV1su411y71xspm_id_from=333.999.0.0
- 一位小白踏入Linux内核补丁提交大门的真实体验
- 简说 套接字缓存的内存空间布局
- 从read开始分析系统调用的上下文切换
- 今晚8点直播 - 闪存友好型文件系统的基础与优化
- 通过性能指标学习Linux Kernel - (下)
- Linux下用户程序的data段和bss段
- Linux CFS调度算法-虚拟时间
- 系统调用角度看用户栈与内核栈切换
- 当DirectIO遇到Loop设备
- Linux内核基础-进程用户栈与内核栈
- Linux内核网络收包角度—浅入中断(2)
- 揭秘 BPF map 前生今世
- 高温预警 今晚8点 eBPF工作原理浅析 边听边练
- Linux内核网络收包角度——浅入中断(1)
- 可观测性-不是你想的那样
- Linux Kdump 机制详解
- Linux OOM 基本原理解析
- Linux Tracing System浅析和eBPF开发经验分享
- [译] BPF ring buffer:使用场景、核心设计及程序示例
- 直播回放总结 | eBPF简介