eBPF 核心技术与实战
倪朋飞
资深 Linux 专家,Kubernetes 项目维护者
10452 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已更新 26 讲/共 37 讲
eBPF 核心技术与实战
15
15
1.0x
00:00/00:00
登录|注册

08|内核跟踪(下):开发内核跟踪程序的进阶方法

你好,我是倪朋飞。
上一讲,我带你梳理了查询 eBPF 跟踪点的常用方法,并以短时进程的跟踪为例,通过 bpftrace 实现了内核跟踪点的跟踪程序。
bpftrace 简单易用,非常适合入门,可以带初学者轻松体验 eBPF 的各种跟踪特性。但在上一讲的案例中,你也发现 bpftrace 并不适用于所有的 eBPF 应用,它本身的限制导致我们无法在需要复杂 eBPF 程序的场景中使用它。在复杂的应用中,我还是推荐你使用 BCC 或者 libbpf 进行开发。
那么,今天我就带你看看,如何使用 BCC 和 libbpf 这两个进阶方法来开发内核跟踪程序。

BCC 方法

我们先来看看如何使用 BCC 来开发上一讲中短时进程的跟踪程序。这里先说明下,由于  execveat  的处理逻辑同  execve  基本相同,限于篇幅的长度,接下来的 BCC 和 libbpf 程序都以  execve  为例。
这里我们先回顾下 03 讲 的内容,使用 BCC 开发的 eBPF 程序包含两部分:
第一部分是用 C 语言开发的 eBPF 程序。在 eBPF 程序中,你可以利用 BCC 提供的库函数和宏定义简化你的处理逻辑。
第二部分是用 Python 语言开发的前端界面,其中包含 eBPF 程序加载、挂载到内核函数和跟踪点,以及通过 BPF 映射获取和打印执行结果等部分。在前端程序中,你同样可以利用 BCC 库来访问 BPF 映射。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文介绍了使用BCC和libbpf两种进阶方法来开发内核跟踪程序。在使用BCC方法时,作者详细讲解了数据结构的定义,包括哈希映射和性能事件映射的创建,以及入口跟踪点处理的步骤,包括获取进程PID和名称,处理命令行参数的方法。通过代码示例和详细注释,读者能够清晰地了解每一步的操作和原理。文章还介绍了使用Python前端处理eBPF程序的步骤,并提到了BCC在加载字节码时会自动挂载跟踪点的特性。另外,文章还提到了在生产环境中使用BCC的难题,并探讨了使用libbpf进行eBPF程序开发的方法。作者详细介绍了使用libbpf开发eBPF程序的四个步骤,并给出了一个简化版本的Makefile示例。整体而言,本文内容详实,适合对eBPF有一定了解并希望深入学习的读者阅读。文章还总结了bpftrace、BCC和libbpf这三种方法的使用场景和特点,为读者提供了全面的eBPF程序开发方法选择建议。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《eBPF 核心技术与实战》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(24)

  • 最新
  • 精选
  • 莫名
    1、对于参数问题,当前 BPF 程序尝试把所有参数一次性放入,受限于栈最大长度 512,很容易出现被截断现象。以 BCC 程序为例的解决方法:遍历参数列表 argv 时,每个参数读取之后直接调用 perf_submit 提交至 ringbuf,而不是读取所有参数后仅提交一次,最后用户态程序负责把这些字符串拼接起来。这样可以做到参数最大个数不受限制,且每个参数长度可接近栈最大长度 512(当前 BPF 程序限制 64 容易被截断)。 另一个解决方式应该可以采用 perf-cpu array 映射类型,避免占用有限的栈空间,具体没尝试过。 2、以 BCC 程序为例获取父进程,struct data_t 增加 ppid 字段,然后由 task->real_parent->tgid 赋值。 struct task_struct *task; task = (struct task_struct *)bpf_get_current_task(); data.ppid = task->real_parent->tgid;

    作者回复: 非常棒的答案,赞一个👍

    2022-02-02
    3
    11
  • piboye
    golang + libbpf 是不是比BCC 方案更好?

    作者回复: 从最终部署的角度来看是的,但对入门者来说BCC更简单一些

    2022-03-15
    4
    1
  • 不了峰
    请教一下文章中关于 「libbpf 方法」 为什么 文章中没有提及 execsnoop.h 这个文件。。 而 execsnoop.bpf.c 要 #include "execsnoop.h" 这个文件 ? 关于 execsnoop.h 文件内容生成,是不是在前面的几章有涉及?

    作者回复: 这个文件的内容不是自动生成的,跟C语言一样,头文件用来放类型定义和常量定义。 文件的源码见 https://github.com/feiskyer/ebpf-apps/blob/main/bpf-apps/execsnoop.h

    2022-02-21
    1
  • 写点啥呢
    请教下老师: 1. bpf的辅助函数执行上下文是在当前进程下么,因为我看像execsnoop例子中get_current_pid_tgid调用能拿到的是新启动进程pid。 2. libbpf的开发模式能看到从编译器到bpf在背后做了很多工作,如果想了解bpf程序的实现,比如宏是如何定义了映射和挂载点,程序加载的时候如何从程序段中的信息实现的映射创建和挂载,老师能否给指一个学习路径?谢谢啦

    作者回复: 1. eBPF程序是在内核空间执行,可以在内核空间获得进程的上下文信息。 2. 这些具体的步骤可以参考 https://github.com/libbpf/libbpf。比如,https://github.com/libbpf/libbpf/blob/master/src/libbpf.c#L8599-L8675 这儿定义了所有不同程序类型所对应的 SEC 名称及挂载方式。

    2022-02-03
    3
    1
  • 龍蝦
    最新的代码 https://github.com/feiskyer/ebpf-apps/tree/b89ae0f 执行 make 出错,这是 gcc-toolset 导致吗?老师,各位同 学是否遇到过? ``` [root@Rocky-9 bpf-apps]# make clang -g -O2 -target bpf -D__TARGET_ARCH_x86 -Ilibbpf/usr/include -I../libbpf/include/uapi -I/usr/include/x86_64-linux-gnu -I. -c hello.bpf.c -o hello.bpf.o /usr/sbin/bpftool gen skeleton hello.bpf.o > hello.skel.h clang -g -O2 -Wall -Ilibbpf/usr/include -I../libbpf/include/uapi -I/usr/include/x86_64-linux-gnu -I. -c hello.c -o hello.o clang -Wall -O2 -g hello.o -static /root/ebpf-apps/bpf-apps/libbpf/libbpf.a -lelf -lz -o hello /usr/bin/ld: cannot find -lelf /usr/bin/ld: cannot find -lz /usr/bin/ld: cannot find -lc clang-14: error: linker command failed with exit code 1 (use -v to see invocation) make: *** [Makefile:14: hello] Error 1 [root@Rocky-9 bpf-apps]# [root@Rocky-9 bpf-apps]# clang -v clang version 14.0.6 (Red Hat 14.0.6-4.el9_1) Target: x86_64-redhat-linux-gnu Thread model: posix InstalledDir: /usr/bin Found candidate GCC installation: /opt/rh/gcc-toolset-12/root/usr/lib/gcc/x86_64-redhat-linux/12 Selected GCC installation: /opt/rh/gcc-toolset-12/root/usr/lib/gcc/x86_64-redhat-linux/12 Candidate multilib: .;@m64 Candidate multilib: 32;@m32 Selected multilib: .;@m64 ```

    作者回复: 这是依赖库没有安装导致的,请参考README的步骤安装依赖库

    2023-02-21归属地:江苏
    2
  • 从远方过来
    老师,我运行下面的代码,在另外一个shell上面执行chmod命令,但是bcc却没有输出,请问这个是什么原因? 内核版本: 4.14.15-1.el7.elrepo.x86_64 操作系统: CentOS Linux release 7.9.2009 (Core) bcc版本:bcc-0.21.0-1.el7.x86_64 bcc-tools-0.21.0-1.el7.x86_64 python-bcc-0.21.0-1.el7.noarch #!/usr/bin/env python3 # Tracing execve() system call. from bcc import BPF from bcc.utils import printb chmod_prog = """ /* Tracing execve system call. */ #include <uapi/linux/ptrace.h> #include <linux/sched.h> #include <linux/fs.h> // perf event map (sharing data to userspace) and hash map (sharing data between tracepoints) struct data_t { u32 pid; umode_t mode; char * filename; }; BPF_PERF_OUTPUT(events); // 生成一个event事件 // BPF_HASH(tasks, u32, struct data_t); // sys_enter_chmod tracepoint. TRACEPOINT_PROBE(syscalls, sys_enter_chmod) { struct data_t data = {}; u32 pid = bpf_get_current_pid_tgid(); umode_t mode = args->mode; char * filename = (char *)args->filename; data.pid = pid ; data.mode = mode; data.filename = filename; events.perf_submit(args, &data, sizeof(data)); return 0; } """ # 1) load BPF program b = BPF(text=chmod_prog) # b = BPF(src_file="chmod.c") # 2) print header print("%-6s %-16s %-3s %s" % ("PID", "COMM", "RET", "ARGS")) # 3) define the callback for perf event def print_event(cpu, data, size): # event data struct is generated from "struct data_t" by bcc event = b["events"].event(data) printb(b"%-6d %-16s %-3d" % (event.pid, event.mode, event.filename)) # 4) loop with callback to print_event b["events"].open_perf_buffer(print_event) while 1: try: b.perf_buffer_poll() except KeyboardInterrupt: exit()

    作者回复: 有时候命令名跟syscall名字不是一致的,比如chmod命令很可能使用的是fchmodat而不是chmod系统调用

    2022-03-17
    2
  • heyhd9475
    老师你好,我想请问为什么每次使用bpf_probe_read_user_str()读取后不在每次读取到的参数中间加上一个空格呢,我这边尝试加这个空格,为什么会提示如下的无效无限制可变偏移量栈写入呢: invalid unbounded variable-offset write to stack R2 报错的源码为: data.args[data.next_arg_index-1]=' '; 是因为bcc认为参数data.next_arg_index是没有限制范围的,不安全的吗。

    作者回复: 看起来是验证器认为它是一个无界的变量,你的内核版本和BCC版本是什么?

    2022-02-19
  • hjydxy
    老师好: 在这个 execsnoop.skel.h 文件中,有一个“execsnoop_bpf__load”函数,你在说明中也说了这个函数是加载ebpf程序用的,我想请教下这个函数和/samples/bpf/下例子中使用的“bpf_object__load”有什么区别和联系,我的理解是这两个函数的作用差不多,都是加载ebpf程序用的,既然如此,使用“execsnoop_bpf__load”的意义或者好处在哪里?谢谢。

    作者回复: 这是一个封装后的函数,使用起来更简单(当然直接用bpf_object__load也是没问题的)。

    2022-02-16
  • c1
    请教: Ubuntu 21.10 Linux u21 5.13.0-28-generic #31-Ubuntu SMP Thu Jan 13 17:41:06 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux 执行execsnoop.py报错: ... /virtual/main.c:47:42: error: incomplete definition of type 'struct tracepoint__syscalls__sys_enter_execve' const char **argv = (const char **)(args->argv); ... /virtual/main.c:82:22: error: incomplete definition of type 'struct tracepoint__syscalls__sys_exit_execve' data->retval = args->ret; ...

    作者回复: 有没有引入头文件?完整的程序可以参考Github: https://github.com/feiskyer/ebpf-apps/blob/main/bcc-apps/python/execsnoop.c#L2-L4。

    2022-02-09
    2
  • Haric
    文章提到,使用libbfp方式在开启了 BTF 的其他机器都可以运行,请问在嵌入式设备(需要交叉编译)能运行吗?

    作者回复: 如果内核也支持BTF的话,理论上应该是可以运行的;但如果内核版本比较旧的话,可能不支持BTF,这时候可以使用条件编译的方式,为旧版本内核关闭BTF。

    2022-02-03
收起评论
显示
设置
留言
24
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部