简介

本章基于上一节测试代码修改。

生成skel文件

skel文件本质是一个C语言的头文件,头文件内包含了一个用于操作BPF程序和maps的结构体,同时还包含一些用于加载和管理BPF对象的辅助函数。

在实际应用中,可以在用户程序中包含该文件,然后使用其中的函数和结构体来加载和操作BPF程序和maps,而无需手动编写复杂的BPF系统调用代码,这样可以大幅简化BPF程序的加载和使用。

生成头文件使用如下命令:

bpftool gen skeleton <BPF 二进制文件> > <要写入的文件> [name 模块名]

最后的[name 模块名]用于指定生成的文件中结构体的名称,通过该参数可以使用不同的bpf内核程序生成相同的骨架代码,达到应用程序代码复用的目的。

vmlinux.h

vmlinux.h是使用工具生成的代码文件,它包含了系统运行 Linux 内核源码中使用的所有类型定义,所以在BPF内核程序包含该文件,就可以使用所有数据类型定义,生成该文件的命令为:

bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h

注意:由于该文件是当前运行内核生成的,所以试图将编译好的eBPF程序迁移到另一台运行不同版本内核的机器上运行,可能会面临崩溃的问题。

该文件的开头和最后有这里两行代码

#pragma clang attribute push (__attribute__((preserve_access_index)), apply_to = record)
.......
#pragma clang attribute pop
  • preserve_access_index属性告诉Clang位类型定义生成CO-RE重定位信息

  • clang attribute push和clang attribute pop成对出现,表示push后的属性应用到push和pop之间的所有类型

修改hello程序

  1. 修改内核程序hello_kernel.c

     /**
      * 包含linux内核所有数据结构
      */
     #include "vmlinux.h"
     ​
     #include <bpf/bpf_helpers.h>
     ​
     SEC("tracepoint/syscalls/sys_enter_execve")
     int hello_bpf(void *ctx)
     {
         bpf_printk("%s\n", "hello BPF!");
     ​
         return 0;
     }
     ​
     char _license[] SEC("license") = "GPL";
  2. 修改应用程序hello_user.c

     #include <stdio.h>
     #include <unistd.h>
     ​
     /**
      * 包含 bpftool 自动生成的骨架代码
      */
     #include "hello_kernel.h"
     ​
     /**
      * 不管libbpf的日志级别,打印加载过程的详细日志
      */
     static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args)
     {
         return vfprintf(stderr, format, args);
     }
     ​
     int main(void)
     {
         /**
          * 设置一个用户自定义的回调函数,用于处理 libbpf 的日志输出
          */
         libbpf_set_print(libbpf_print_fn);
     ​
         /**
          * 调用生成的代码,打卡并加载BPF程序
          */
         struct hello_kernel *obj = hello_kernel__open_and_load();
         if (!obj) {
             printf("open and load error!\n");
             return 0;
         }
     ​
         /**
          * 绑定BPF程序
          */
         int ret = hello_kernel__attach(obj);
         if (ret) {
             printf("attch error!\n");
             hello_kernel__detach(obj);
             return 1;
         }
     ​
         printf("Successfully started! Please run `sudo cat /sys/kernel/debug/tracing/trace_pipe`"
                "to see output of the BPF programs.\n");
     ​
         while (1) {
             sleep(1);
         }
     ​
         /**
          * 卸载BPF
          */
         hello_kernel__detach(obj);
     ​
         return 0;
     }
  3. 修改Makefile

     CLANG ?= clang
     LLVM_STRIP ?= llvm-strip
     BPFTOOL ?= bpftool
     ARCH ?= x86
     ​
     all : vmlinux.h hello_kernel.o hello_kernel.h hello
     ​
     vmlinux.h:
         $(BPFTOOL) btf dump file /sys/kernel/btf/vmlinux format c > $@
     ​
     hello_kernel.o: hello_kernel.c
         $(CLANG) -target bpf -I./ -D__TARGET_ARCH_$(ARCH) -O2 -g -c $< -o $@
         $(LLVM_STRIP) -g $@
     ​
     hello_kernel.h: hello_kernel.o
         $(BPFTOOL) gen skeleton -p $< > $@
     ​
     hello: hello_user.c
         gcc -o $@ $< -I./ -lbpf
     ​
     clean:
         rm -f *.o hello vmlinux.h hello_kernel.h
  4. 编译程序

     make

    成功执行后,会在当前目录生成hello(用户应用程序)、hello_kernel.h(依据BPF二进制文件生成的骨架代码)、hello_kernel.o(BPF二进制文件)、vmlinux.h(包含所有linux内核的数据结构文件)。

  5. 运行程序以及查看输出

    与之前一

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐