从零构建libbpf+ebpf CO-RE程序


背景

今年来bpf在内核发展迅速,从一开始的网络包过滤,发展到现在可用于trace,TC,权限控制等等,现在Facebook的工程师也将其引入到调度子系统,可见bpf发展迅速。通常我们可以使用BCC (BPF Compiler Collection)来快速的开发原型或者实验工具,通过BCC提供的一套框架,将程序部署之后,BCC首先会唤醒其嵌入的Clang/LLVM编译器提取本地内核头文件(必须确保已从正确的kernel-devel软件包中将其安装在系统上),并即时进行编译。这种方式存在以下问题:

  1. Clang/LLVM组合是一个很大的库,导致发布的应用的库会比较大;
  2. Clang/LLVM组合使用的资源比较多,因此当编译的BPF代码启动时会消耗大量资源,可能会推翻已均衡的生产负载;
  3. BPF程序的测试和开发迭代也相当痛苦,因为一旦重新编译并重启用户空间控制应用程序,甚至会在运行时遇到各种琐碎的编译错误。这无疑会增加难度,且无益于快速迭代。

一、libbpf和BPF CO-RE

为了解决以上提到的资源占用、兼容性等问题,社区提出了BPF CO-RE(Compile Once, Run Everywhere),通过用户空间的BPF加载器库(libbpf),和编译器(Clang)。通过这些组件来支持编写可移植的BPF程序,使用相同的预编译的BPF程序来处理不同内核之间的差异。更加详细的信息可参考:https://facebookmicrosites.github.io/bpf/blog/2020/02/19/bpf-portability-and-co-re.html。
网上也有大量介绍该特性的文档,本文将会使用一个简单的示例,以命令行的方式构建使用libbpf的应用程序,方便理解相关工程的Makefile或者自己搭建环境。

二、确认配置依赖

本次实验使用内核版本为5.10版本:

[root@localhost ~]# uname -r
5.10.0+

1.开启BTF特性

确认内核开启BTF特性,以下两个方式均可,如果没有打开,需要开启该选项并重新编译内核(如果编译过程中出现BTF的一个报错,尝试安装dwraves、libdwarves-devel包解决)。

# zcat /proc/config.gz | grep BTF
CONFIG_DEBUG_INFO_BTF=y
# file /sys/kernel/btf/vmlinux
/sys/kernel/btf/vmlinux: data

2.依赖包

CO-RE构建方式需要安装bpftool、libbpf、libbpf-devel、elf-utils、kernel-source。

三、构建过程

1.生成vmlinux

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

2.编写minimal.bpf.c,代码如下

#include <linux/bpf.h>
#include <bpf/bpf_helpers.h> // 需要包含的头文件

char LICENSE[] SEC("license") = "Dual BSD/GPL"; // SEC宏会将此字符串放到一个elf文件中的独立段里面供加载器检查

SEC("tp/syscalls/sys_enter_write") // 此处使用的tracepoint,在每次进入write系统调用的时候触发
int handle_tp(void *ctx)
{
        bpf_printk("BPF triggered from PID %d.\n", pid); //可cat /sys/kernel/debug/tracing/trace查看调试信息
        return 0;
}

3. 将minimal.bpf.c编译成minimal.bpf.o

# clang -g -O2 -target bpf -D__TARGET_ARCH_x86 -I/usr/src/kernels/$(uname -r)/include/uapi/ -I/usr/src/kernels/$(uname -r)/include/ -I/usr/include/bpf/ -c minimal.bpf.c -o minimal.bpf.o

4. 将minimal.bpf.o转换为minimal.skel.h

# bpftool gen skeleton minimal.bpf.o > minimal.skel.h

查看minimal.skel.h中的内容,大概就知道libbpf是怎么做的兼容了。

5. 编写用户程序minimal.c

#include <stdio.h>
#include <unistd.h>
#include <sys/resource.h> // rlimit使用
#include <bpf/libbpf.h>
#include "minimal.skel.h" // 这就是上一步生成的skeleton,minimal的“框架” 

int main(int argc, char **argv)
{
        struct minimal_bpf *skel; // bpftool生成到skel文件中,格式都是xxx_bpf。
        int err;

		struct rlimit rlim = {
        	.rlim_cur = 512UL << 20,
        	.rlim_max = 512UL << 20,
    	};
		// bpf程序需要加载到lock memory中,因此需要将本进程的lock mem配大些
    	if (setrlimit(RLIMIT_MEMLOCK, &rlim)) {
        	fprintf(stderr, "set rlimit error!\n");
        	return 1;
    	}
        // 第一步,打开bpf文件,返回指向xxx_bpf的指针(在.skel中定义)
        skel = minimal_bpf__open();
        if (!skel) {
                fprintf(stderr, "Failed to open BPF skeleton\n");
                return 1;
        }

       // 第二步,加载及校验bpf程序
        err = minimal_bpf__load(skel);
        if (err) {
                fprintf(stderr, "Failed to load and verify BPF skeleton\n");
                goto cleanup;
        }

        // 第三步,附加到指定的hook点
        err = minimal_bpf__attach(skel);
        if (err) {
                fprintf(stderr, "Failed to attach BPF skeleton\n");
                goto cleanup;
        }

        printf("Successfully started! Please run `sudo cat /sys/kernel/debug/tracing/trace_pipe` "
               "to see output of the BPF programs.\n");

        for (;;) {
                /* trigger our BPF program */
                fprintf(stderr, ".");
                sleep(1);
        }
cleanup:
        minimal_bpf__destroy(skel);
        return -err;
}

6.编译用户态程序minimal.o

# gcc -I/usr/src/kernels/$(uname -r)/include/uapi/ -I/usr/src/kernels/$(uname -r)/include/ -I/usr/include/bpf/ -c minimal.c -o minimal.o

7.链接成为可执行程序minimal

# gcc minimal.o -lbpf -lelf -lz -o minimal

至此,可执行程序便生成了出来。知道怎么构建的,搭建相关的工程不就是将xxx封装到cmake或者Makefile中了吗!
sample代码来源:https://gitee.com/mirrors_libbpf/libbpf-bootstrap

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值