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的运行状态

ebpf-dev-01

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 协议 ,转载请注明出处!