eBPF应用开发

1. eBPF应用开发流程

  1. 使用 C 语言开发一个 eBPF 程序
  2. 借助 LLVM 把 eBPF 程序编译成 BPF bytecode
  3. 通过 bpf 系统调用,把 BPF bytecode提交给内核
  4. 内核验证并运行 BPF bytecode,并把相应的状态保存到 BPF maps中
  5. 用户程序通过 BPF maps查询 BPF bytecode的运行状态

2. 开发实践

BCC 是一个 BPF 编译器集合,包含了用于构建 BPF 程序的编程框架和库,并提供了大量可以直接使用的工具。使用 BCC 的好处是,它把上述的 eBPF 执行过程通过内置框架抽象了起来,并提供了 Python、C++ 等编程语言接口。这样,就可以直接通过 Python 语言去跟 eBPF 的各种事件和数据进行交互。

以跟踪 openat()(即打开文件)这个系统调用为例,看看如何开发并运行第一个 eBPF 程序。

2.1 使用 C 开发一个 eBPF 程序

新建一个 hello.c 文件:

1
2
3
4
5
int hello_world(void *ctx)
{
bpf_trace_printk("Hello, World!");
return 0;
}

bpf_trace_printk() 是一个最常用的 BPF 辅助函数,它的作用是输出一段字符串。不过,由于 eBPF 运行在内核中,它的输出并不是通常的标准输出(stdout),而是内核调试文件 /sys/kernel/debug/tracing/trace_pipe ,可以直接使用 cat 命令来查看这个文件的内容

2.2 使用 Python 和 BCC 库开发一个用户态程序

新建一个 hello.py 文件:

1
2
3
4
5
6
7
8
9
10
#!/usr/bin/env python3
# 1) 导入了 BCC 库的 BPF 模块,以便接下来调用
from bcc import BPF

# 2) 调用 BPF() 加载第一步开发的 BPF 源代码
b = BPF(src_file="hello.c")
# 3) 将 BPF 程序挂载到内核探针(kprobe)其中 do_sys_openat2() 是系统调用 openat() 在内核中的实现
b.attach_kprobe(event="do_sys_openat2", fn_name="hello_world")
# 4) 读取内核调试文件 /sys/kernel/debug/tracing/trace_pipe 的内容,并打印到标准输出中
b.trace_print()

2.3 执行 eBPF 程序

用户态程序开发完成之后,最后一步就是执行它了。需要注意的是, eBPF 程序需要以 root 用户来运行,非 root 用户需要加上 sudo 来执行:

1
2
$ chmod +x hello.py
$ sudo ./hello.py

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!