eBPF 的基本使用,eBPF 学习(一)
eBPF 的基础使用
eBPF 简介:安全和有效地扩展内核
eBPF 是一项革命性的技术,起源于 Linux 内核,可以在操作系统的内核中运行沙盒程序。它被用来安全和有效地扩展内核的功能,而不需要改变内核的源代码或加载内核模块。eBPF 通过允许在操作系统内运行沙盒程序,应用程序开发人员可以在运行时,可编程地向操作系统动态添加额外的功能。然后,操作系统保证安全和执行效率,就像在即时编译(JIT)编译器和验证引擎的帮助下进行本地编译一样。eBPF 程序在内核版本之间是可移植的,并且可以自动更新,从而避免了工作负载中断和节点重启。
今天,eBPF 被广泛用于各类场景:在现代数据中心和云原生环境中,可以提供高性能的网络包处理和负载均衡;以非常低的资源开销,做到对多种细粒度指标的可观测性,帮助应用程序开发人员跟踪应用程序,为性能故障排除提供洞察力;保障应用程序和容器运行时的安全执行,等等。可能性是无穷的,而 eBPF 在操作系统内核中所释放的创新才刚刚开始。
eBPF 的简单使用
- 编写一个 helloworld 程序
安装对应的开发工具
1 | # For Ubuntu20.10+sudo apt-get install -y make clang llvm libelf-dev libbpf-dev bpfcc-tools libbpfcc-dev linux-tools-$(uname -r) linux-headers-$(uname -r) |
在开发 eBPF 程序之前,我们先来看一下 eBPF 的开发和执行过程。一般来说,这个过程分为以下 5 步:
第一步,使用 C 语言开发一个 eBPF 程序;
第二步,借助 LLVM 把 eBPF 程序编译成 BPF 字节码;
第三步,通过 bpf 系统调用,把 BPF 字节码提交给内核;
第四步,内核验证并运行 BPF 字节码,并把相应的状态保存到 BPF 映射中;
第五步,用户程序通过 BPF 映射查询 BPF 字节码的运行状态。
这里我们使用 bcc 来开发 eBPF 程序,如果使用 bcc 来开发 eBPF 程序的话,可以将前面所说的五步简化为三步
第一步:使用 C 开发一个 eBPF 程序
- 新建一个
hello.c
文件,并输入下面的内容:
1 | int hello_world(void *ctx) |
输出 Hello,World,由于是在内核中进行运行所以结果输出到内核调试文件 /sys/kernel/debug/tracing/trace_pipe
第二步:使用 Python 和 BCC 库开发一个用户态程序
接下来,创建一个 hello.py 文件,并输入下面的内容
1 | #!/usr/bin/env python3 |
让我们来看看每一处的具体含义:
第 1) 处导入了 BCC 库的 BPF 模块,以便接下来调用;
第 2) 处调用 BPF() 加载第一步开发的 BPF 源代码;
第 3) 处将 BPF 程序挂载到内核探针(简称 kprobe),其中 do_sys_openat2() 是系统调用 openat() 在内核中的实现;
第 4) 处则是读取内核调试文件 /sys/kernel/debug/tracing/trace_pipe
的内容,并打印到标准输出中。
第三步:执行 eBPF 程序
需要以 root 用户权限执行
1 | sudo python3 hello.py |
输出如图
这就是一个简单的 eBPF 程序了,但是我们可以看到这个 eBPF 程序当中有几个缺点
- 既然我们是通过 ebpf 监控系统打开文件的操作,那么正常情况输出结果应该有打开的文件名
- 现在读取结果是直接读
/sys/kernel/debug/tracing/trace_pipe
这个内核调试文件的,这就有一个问题,当存在多个 ebpf 程序的时候输出结果都是到这个文件,这样的话就使得输出结果很不清晰,并且这样性能也很差
针对上述两个问题,需要利用 BPF 映射来结果,通过映射来读取 ebpf 执行的结果并适当进行一些数据处理,因为上述截图中很多输出字段都是我们不需要的
改进第一个 eBPF 程序
BPF 程序可以利用 BPF 映射(map)进行数据存储,而用户程序也需要通过 BPF 映射,同运行在内核中的 BPF 程序进行交互。
所以,为了解决上面提到的第一个问题,即获取被打开文件名的问题,我们就要引入 BPF 映射。
BCC 定义了一系列的库函数和辅助宏定义。这里使用 BPF_PERF_OUTPUT
来定义一个 Perf 事件类型的 BPF 映射,这里先定义一个数据结构
1 | // 包含头文件 |
然后,在 eBPF 程序中,填充这个数据结构,并调用 perf_submit()
把数据提交到刚才定义的 BPF 映射中:
1 | // 定义kprobe处理函数 |
合起来就是下面这个文件,该文件最终在内核中进行运行,将采集到的数据输出到映射中
1 | // 包含头文件 |
然后我们需要编写一个用户态的调用程序
1 | from bcc import BPF |
小结
简单接触一下 eBPF 的使用
Ref
极客时间 eBPF 核心技术与实战
- 本文标题:eBPF 的基本使用
- 创建时间:2023-07-04 10:11:14
- 本文链接:2023/07/04/eBPF-的基本使用/
- 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!