• eBPF理解 (一)


    目录

    一、eBPF简介

    二、快速实现BPF程序

    三、使用bpf映射


    一、eBPF简介

    eBPF 是从 BPF (Berkeley Packet Filter) 技术扩展而来的

    eBPF 系统启动后就一直运行在那里,它需要事件触发后才会执行。借助kprobe和uprobe,eBPF 程序几乎可以在内核和应用的任意位置进行插桩。

    eBPF 的诞生成为内核的一个顶级子系统最典型的就是 iovisor 带来的 BCC、bpftrace 等工具。

    图片来源Linux Extended BPF (eBPF) Tracing Tools

    BPF开发过程过程

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

    验证过程

    • 只有特权进程才可以执行 bpf 系统调用;
    • BPF 程序不能包含无限循环;
    • BPF 程序不能导致内核崩溃;
    • BPF 程序必须在有限时间内完成。

    映射过程,来源,eBPF 程序的运行需要历经编译、加载、验证和内核态执行等过程,而用户态程序则需要借助 BPF 映射来获取内核态 eBPF 程序的运行状态。

    安装方式 ubuntu22.04上

    sudo apt-get install -y  make clang llvm libelf-dev libbpf-dev bpfcc-tools libbpfcc-dev linux-tools-$(uname -r) linux-headers-$(uname -r)

    二、快速实现BPF程序

    使用BCC开发eBPF程序,跟踪openat系统调用

    1、编辑C程序

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

    新建hello_world.c 文件,

    输出Hello, World!打印

    bpf_trace_printk时BPF提供的输出,可以用cat /sys/kernel/debug/tracing/trace_pipe查看

    2、编辑python程序

    1. from bcc import BPF
    2. b = BPF(src_file="hello_world.c")
    3. b.attach_kprobe(event="do_sys_openat2", fn_name="hello_world")
    4. b.trace_print()

     python语言,导入BCC模块的BPF

    调用BPF加载.c文件

    将BPF程序挂在到内核探针kprobe上,event是系统调用openat在内核的实现

    打印输出,读取内核中/sys/kernel/debug/tracing/trace_pipe的内容

    3、运行eBPF程序

    1. root@root:/# python3 hello.py
    2. b'CPU:1 [LOST 127 EVENTS]'
    3. b' systemd-1 [001] d...1 3141.925409: bpf_trace_printk: Hello, World!'
    4. b' systemd-1 [001] d...1 3141.925410: bpf_trace_printk: Hello, World!'
    5. b' systemd-1 [001] d...1 3141.925415: bpf_trace_printk: Hello, World!'
    6. b' systemd-1 [001] d...1 3141.925416: bpf_trace_printk: Hello, World!'
    7. b' systemd-1 [001] d...1 3141.925417: bpf_trace_printk: Hello, World!'
    8. b' systemd-1 [001] d...1 3141.925419: bpf_trace_printk: Hello, World!'
    9. b' systemd-1 [001] d...1 3141.925421: bpf_trace_printk: Hello, World!'
    10. b' systemd-1 [001] d...1 3141.925423: bpf_trace_printk: Hello, World!'
    11. b' systemd-1 [001] d...1 3141.925430: bpf_trace_printk: Hello, World!'
    12. b' systemd-1 [001] d...1 3141.925442: bpf_trace_printk: Hello, World!'

    systemd-1 表示进程的名字

    [001]表示CPU编号

    d...1是选项

    3141.9254xx是时间戳

    bpf_trace_printk是函数名

    Hello,World是打印输出

    三、使用bpf映射

    BCC定义的库函数和辅助定义

    BPF_PERF_OUTPUT函数功能

    创建一个 BPF 表,用于通过性能环缓冲区将自定义事件数据推送到用户空间。这是将每个事件的数据推送到用户空间的首选方法。

    1、改进的.c文件

    1. //头文件
    2. #include
    3. #include
    4. //结构体
    5. struct data_t {
    6. u32 pid;
    7. u64 ts;
    8. char comm[TASK_COMM_LEN];
    9. char fname[NAME_MAX];
    10. };
    11. //定义性能事件映射
    12. BPF_PERF_OUTPUT(events);
    13. //处理函数
    14. int hello(struct pt_regs *ctx, int dfd, const char __user * filename, struct open_how *how) {
    15. struct data_t data = {};
    16. //获取PID和时间
    17. data.pid = bpf_get_current_pid_tgid();
    18. data.ts = bpf_ktime_get_ns();
    19. //获取进程名,把进程名复制到预定义的缓冲区
    20. if (bpf_get_current_comm(&data.comm, sizeof(data.comm)) == 0)
    21. { //读取进程打开的文件名
    22. bpf_probe_read(&data.fname, sizeof(data.fname), (void *)filename);
    23. }
    24. //提交性能事件
    25. events.perf_submit(ctx, &data, sizeof(data));
    26. return 0;
    27. }

    有映射后,bpf_trace_printk就不需要了,用户可以直接从内核态读取数据。

    2、改进的python

    将C语言写在一起,参考hello_perf_outputpy

    1. from bcc import BPF
    2. # define BPF program
    3. prog = """
    4. #include
    5. // define output data structure in C
    6. struct data_t {
    7. u32 pid;
    8. u64 ts;
    9. char comm[TASK_COMM_LEN];
    10. char fname[NAME_MAX];
    11. };
    12. BPF_PERF_OUTPUT(events);
    13. int hello(struct pt_regs *ctx,int dfd,const char __user *filename,struct open_how *how) {
    14. struct data_t data = {};
    15. data.pid = bpf_get_current_pid_tgid();
    16. data.ts = bpf_ktime_get_ns();
    17. if(bpf_get_current_comm(&data.comm, sizeof(data.comm)) == 0)
    18. {
    19. bpf_probe_read(&data.fname,sizeof(data.fname),(void *)filename);
    20. }
    21. events.perf_submit(ctx, &data, sizeof(data));
    22. return 0;
    23. }
    24. """
    25. # load BPF program
    26. b = BPF(text=prog)
    27. b.attach_kprobe(event=b.get_syscall_fnname("openat"), fn_name="hello")
    28. # header
    29. print("%-18s %-16s %-6s %s" % ("TIME(s)", "COMM", "PID", "MESSAGE"))
    30. # process event
    31. start = 0
    32. def print_event(cpu, data, size):
    33. global start
    34. event = b["events"].event(data)
    35. if start == 0:
    36. start = event.ts
    37. time_s = (float(event.ts - start)) / 1000000000
    38. print("%-18.9f %-16s %-6d %s" % (time_s, event.comm, event.pid,
    39. "Hello, perf_output!"))
    40. # loop with callback to print_event
    41. b["events"].open_perf_buffer(print_event)
    42. while 1:
    43. b.perf_buffer_poll()

    open_perf_buffer定义了名为event的perf映射事件,通过循环调用perf_buffer_poll读取映射的内容。

    3、运行eBPF程序

    1. root@root/# python3 hello_perf.py
    2. TIME(s) COMM PID MESSAGE
    3. 0.000000000 b'systemd-oomd' 788 b'/proc/meminfo'
    4. 0.250287922 b'systemd-oomd' 788 b'/proc/meminfo'
    5. 0.500141038 b'systemd-oomd' 788 b'/proc/meminfo'
    6. 0.500233681 b'systemd-oomd' 788 b'/sys/fs/cgroup/user.slice/user-1000.slice/user@1000.service/memory.pressure'
    7. 0.500300281 b'systemd-oomd' 788 b'/sys/fs/cgroup/user.slice/user-1000.slice/user@1000.service/memory.current'
    8. 0.500315337 b'systemd-oomd' 788 b'/sys/fs/cgroup/user.slice/user-1000.slice/user@1000.service/memory.min'

    参考:​​​​​​​

    bcc/reference_guide.md at master · iovisor/bcc · GitHub

    What is eBPF? An Introduction and Deep Dive into the eBPF Technology

    Linux eBPF Tracing Tools (brendangregg.com)

  • 相关阅读:
    Vue3 Pinia 全局状态管理工具的使用
    【node进阶】浅析Koa框架---ejs模板|文件上传|操作mongoDB
    代码随想录二刷day28
    nodejs与后端
    Java String类
    蜂蜡/聚苯乙烯大孔树脂微球/硫酸盐修饰聚苯乙烯微球/草莓状银/聚苯乙烯-丙烯腈复合微球的研究
    自动化测试---即selenium
    刷题知识回顾《二》LRU缓存详解
    2024龙年特别篇 -- 魔法指针 之 指针变量
    MySQL的优化?
  • 原文地址:https://blog.csdn.net/WANGYONGZIXUE/article/details/126437354