Loading

eBPF的 HelloWorld 示例

前言

极客时间 eBPF 核心技术与实战 的学习笔记.
本章简单写一个入门的 ebpf 程序

环境安装

每个版本都不太一样, 所以这里只是做个参考
如果在安装环境上遇到问题只能自行解决了

我自己使用的是 debian12

root@VM-4-12-debian:~/ebpf# uname -a
Linux VM-4-12-debian 6.1.0-28-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.119-1 (2024-11-22) x86_64 GNU/Linux

推荐安装最新或者次版本

# 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)

# For RHEL8.2+
sudo yum install libbpf-devel make clang llvm elfutils-libelf-devel bpftool bcc-tools bcc-devel

helloworld

教程以 python+c 作为例子
代码意思配合注释看

在某个目录中新建两个文件

hello.c(内核态代码)

// 定义 hello 函数
int hello_world(void *ctx)
{
    // bpf_trace_printk 打印输出到 调试文件
    bpf_trace_printk("Hello, World!");
    return 0;
}

hello.py(用户态代码)

# 加载 bcc 包,需要先 pip install bcc
from bcc import BPF

# 2) 加载 hello.c 代码
b = BPF(src_file="hello.c")
# 3) 挂载到 BPF 系统
# 指定在 do_sys_openat2 事件发生时, 触发 hello.c 中的 hello_world 函数
# do_sys_openat2 在 打开文件 等操作时被触发
b.attach_kprobe(event="do_sys_openat2", fn_name="hello_world")
# 4) 读取和输出内核调试文件 /sys/kernel/debug/tracing/trace_pipe
b.trace_print()

执行

sudo python3 hello.py

结果解析

运行时我们会发现打印了很多信息, 每一行类似

b'python3-28840   [001] ...21  4786.665982: bpf_trace_printk: Hello, World!'

其中:

  • python3-28840 标识调用的进程和 pid
  • [001] 代表 CPU 编号(从0开始)
  • [...21] 代表若干选项
  • [4786.665982] 标识发生时间戳
  • bpf_trace_printk 代表触发的函数名
  • Hello, World! 代表输出的内容
    然而通常我们不会使用这种方式来进行业务的处理, 这是因为 调试日志不仅包含了我们的信息, 还会有一些其他的日志. 另外还可能带来性能问题.

使用 BPF 映射(map)来传输数据

BCC自带的宏和辅助函数可以查看 bcc/docs/reference_guide.md at master · iovisor/bcc
如果是写过 openresty 的开发者可以把他视作 openresty+lua 中的 lua 辅助函数

map.c

// 引入头文件
#include <uapi/linux/openat2.h>  // 文件打开的相关定义
#include <linux/sched.h>  // 进程调度相关

// 自定义的 map 数据结构
struct data_t {
  u32 pid;  // pid
  u64 ts;  // 时间戳
  char comm[TASK_COMM_LEN];  // 进程名数组, TASK_COMM_LEN 是 sched.h 引入的宏
  char fname[NAME_MAX];  // 文件名数组
};

// BCC 自带的宏, 可以将数据发送到用户态代码中
// 通过perf环缓冲区将自定义事件数据推送到用户空间, 自定义其代号为 events
BPF_PERF_OUTPUT(events);

// 定义kprobe处理函数
int hello_world(struct pt_regs *ctx, int dfd, const char __user * filename, struct open_how *how)
{
  // 组装data
  struct data_t data = { };

  // 获取PID和时间
  data.pid = bpf_get_current_pid_tgid();  // BCC 自带的辅助函数, 可以获取当前调用的 pid 相关信息
  data.ts = bpf_ktime_get_ns();  // BCC 自带的辅助函数, 可以获取当前的 nano 时间戳(从系统启动开始算)

  // 获取进程信息
  if (bpf_get_current_comm(&data.comm, sizeof(data.comm)) == 0)  // BCC自带的辅助函数, 获取当前触发的进程名
  {
    bpf_probe_read(&data.fname, sizeof(data.fname), (void *)filename);  // 从指针filename读取文件名
  }

  // 调用 events 发送数据
  events.perf_submit(ctx, &data, sizeof(data));  // 将 data 发送到用户态代码中
  return 0;
}

map.py

from bcc import BPF

# 加载 map.c 到BPF 中, 在 do_sys_openat2 调用时触发
b = BPF(src_file="map.c")
b.attach_kprobe(event="do_sys_openat2", fn_name="hello_world")

# 打印 header 日志
print("%-18s %-16s %-6s %-16s" % ("TIME(s)", "COMM", "PID", "FILE"))

# 定义从内核态代码中获取到数据后的处理逻辑
start = 0
# data 是接受到的数据
# 这三个参数是 BCC 传输定义的, https://github.com/iovisor/bcc/blob/master/docs/reference_guide.md#2-open_perf_buffer
def print_event(cpu, data, size):
    global start
    event = b["events"].event(data)  # events map
    if start == 0:
            start = event.ts
    time_s = (float(event.ts - start)) / 1000000000
    print("%-18.9f %-16s %-6d %-16s" % (time_s, event.comm, event.pid, event.fname))

# events 的数据映射, 获取数据并交由 print_event 函数处理
b["events"].open_perf_buffer(print_event)
while 1:  # 用户态死循环不会被校验器拦截
    try:
        # 从缓冲区中读取数据进行处理
        b.perf_buffer_poll()
    except KeyboardInterrupt:
        exit()

运行发现用户态已经可以接收并处理数据了

root@VM-4-12-debian:~/ebpf# python3 map.py 
TIME(s)            COMM             PID    FILE            
0.000000000        b'YDService'     172339 b'/proc/sys/kernel/random/uuid'
0.000081607        b'YDService'     172339 b'/proc/sys/kernel/random/uuid'
0.352421903        b'barad_agent'   575490 b'/proc/meminfo'
0.352921024        b'barad_agent'   575490 b'/proc/meminfo'
0.353480201        b'barad_agent'   575490 b'/proc/vmstat' 
0.356383610        b'YDService'     172316 b'/proc/575495/cmdline'
posted @ 2024-12-01 20:52  ChnMig  阅读(12)  评论(0编辑  收藏  举报