编辑
2025-01-20
工作知识
0
请注意,本文编写于 137 天前,最后修改于 137 天前,其中某些信息可能已经过时。

目录

一、eBPF的介绍
二、编写eBPF程序
三、总结

我们从kprobe到perf到bpf都提到了调试,这里eBPF是必不可少的一个内容,本文以一个hello world程序为例,以kprobe和kretprobe中观测的do_sys_openat2为例,实践一个最简单的eBPF程序

一、eBPF的介绍

我们知道eBPF是由BPF演进而来,随着内核可观测的逐渐发展,eBPF逐渐火热,已经成为当前最热门的内核技术之一了,我们调试内核的研发,或多或少都有了解eBPF程序。eBPF的主要框图如下:

image.png 关于eBPF的核心原理介绍,用不着我解释,如果想要了解的,可以查看文档如下:

https://ebpf.io/what-is-ebpf/

二、编写eBPF程序

为了编写eBPF,我们需要两个步骤

为eBPF编写c程序来映射一个event 使用python打印这个event的内容 关于bpf map,可以参考

https://github.com/iovisor/bcc/blob/master/docs/reference_guide.md

我以https://github.com/iovisor/bcc/blob/master/examples/tracing/hello_perf_output.py为例,使用了BPF_PERF_OUTPUT定义

为了能够获取pid,time,进程名等信息,这里需要使用bpf的接口,如下参考

https://docs.ebpf.io/linux/helper-function/

故实现代码如下:

#include <uapi/linux/openat2.h> #include <linux/sched.h> struct data_t { u32 pid; u64 ts; char comm[TASK_COMM_LEN]; }; BPF_PERF_OUTPUT(events); int kylin(struct pt_regs *ctx) { struct data_t data = { }; data.pid = bpf_get_current_pid_tgid(); data.ts = bpf_ktime_get_ns(); bpf_get_current_comm(&data.comm, sizeof(data.comm)); events.perf_submit(ctx, &data, sizeof(data)); return 0; }

这里构造了结构体data_t,其内容通过bpf的api填充,并将event映射出去。

c写好了之后,需要编写python,其目的是获取event的结构,然后打印出来,如下:

#!/usr/bin/env python3 from bcc import BPF from bcc.utils import printb b = BPF(src_file="kylin.c") b.attach_kprobe(event="do_sys_openat2", fn_name="kylin") print("%-18s %-16s %-6s" % ("TIME(s)", "COMM", "PID")) start = 0 def print_event(cpu, data, size): global start event = b["events"].event(data) if start == 0: start = event.ts time_s = (float(event.ts - start)) / 1000000000 printb(b"%-18.9f %-16s %-6d" % (time_s, event.comm, event.pid)) b["events"].open_perf_buffer(print_event) while 1: try: b.perf_buffer_poll() except KeyboardInterrupt: exit()

值得注意的是,我这里借助的还是kprobe,因为kprobe和kretprobe我也是观测的"do_sys_openat2",代码借鉴examples/tracing/hello_perf_output.py

至此,代码编写完成,我们直接试验

# python3 kylin.py TIME(s) COMM PID 0.000000000 systemd-journal 313 0.000131833 systemd-journal 313 0.000204458 systemd-journal 313

可以发现,我们正确的抓到了time,comm和pid

# pidof systemd-journald 313

三、总结

根据上面的例子,我们可以初步的了解了eBPF的程序如何编写。