3. ubuntu20.04下eBPF纯C程序的环境搭建与示例

2,719 阅读4分钟

1. 参考资料

在eBPF的学习过程中,主要是跟着Linux内核之旅开源社区进行学习,在此非常感谢Linux内核之旅开源社区让我少走了很多的弯路,这种乐于分享的精神值得敬佩与学习。

高效入门eBPF视频

高效入门eBPF PPT

ubuntu下BPF纯C程序的编写与运行

2. eBPF编程方法分类

eBPF编程方法主要有如下几种,本文主要记录eBPF C编程的环境搭建与程序编写。bcc安装和简单示例在后面会介绍。

image.png

3. 安装ubuntu20.04

相关资料很多,就不重复造轮子了,贴一下资源路径:

VMware虚拟机安装Ubuntu20.04详细图文教程

Ubuntu20.04的iso

VMware Tools安装

4. 安装ubuntu20.04后可能会出现的问题与解决方法

4.1 ubuntu20.04和windows共享文件夹设置

在VMware中为Ubuntu设置共享文件夹

VMware:Ubuntu虚拟机/mnt/hgfs 下没有共享文件夹,解决办法

4.2 更新源sourcelist:

阿里云源

idle@linux:/$ sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak 
//使用aliyun上的源进行替换:
idle@linux:/$ sudo vi /etc/apt/sources.list 
sudo apt-get update 
sudo apt-get upgrade
4.3 ubuntu和windows之间复制与粘贴
sudo apt-get autoremove open-vm-tools 
sudo apt-get install open-vm-tools 
sudo apt-get install open-vm-tools-desktop

然后执行 reboot 命令重启ubuntu

4.4 VMware中Ubuntu虚拟机链接wifi

解决VMware中Ubuntu虚拟机无法连接本机wifi的问题

简单几行命令解决ubuntu右上角的网络连接图标消失的问题

VMware虚拟机Ubuntu无法连接网络的解决办法

5. 搭建eBPF C程序运行环境

5.1 下载linux源码

下载与所安装的ubuntu内核版本相同的linux源码:

1、查看ubuntu内核版本

image.png

2、apt下载,git clone 或者windows上下载好复制过来都可,将源码解压在/usr/src/目录下。我采用的是windows上下载,然后通过共享文件夹发送给ubuntu。linux源码下载地址

image.png

3、在ubuntu的共享文件夹中可看到对应文件

image.png

4、将linux-5.13.tar.gz拷贝到其他文件夹

image.png

5、将linux-5.13.tar.gz解压到/usr/src下

image.png

5.2 安装依赖文件
sudo apt install libncurses5-dev flex bison libelf-dev binutils-dev libssl-dev

image.png

使用以下两条命令分别安装 clang 和 llvm 
sudo apt install clang 
sudo apt install llvm

image.png

image.png

安装成功的标志如下:

image.png

5.3 配置内核
//安装make 
sudo apt install make

对make不了解的可参考学习

在源码根目录/usr/src/linux-5.13下使用make defconfig生成.config文件。可以执行make menuconfig来可视化的查看内核配置选项,目前可不进行内核配置。

//生成.config文件
make defconfig
//关联内核头文件
make headers_install
//编译内核
make -j8

6. 编译内核eBPF样例

Linux内核源码里包含了大量的eBPF示例代码,非常适合学习者阅读和测试。示例代码里基本是kern和user成对出现,也就是对于一个示例来说,分别提供了在内核空间运行的和用户空间运行的程序。

如果对eBPF纯C程序的组成不太了解的话,可参考

高效入门eBPF视频

高效入门eBPF PPT

在源代码根目录下执行make M=samples/bpf,可以在samples/bpf/文件夹中看到已经生成了eBPF的可执行文件。

7. 编写自己的eBPF程序

编写自己的eBPF程序,只需要在samples/bpf/文件夹中编写hello_kern.c,hello_user.c以及修改Makefile即可,最后在源代码根目录下执行make M=samples/bpf,就可以在samples/bpf/文件夹中看到自己的bpf程序生成了可执行文件。

7.1 第一步,编写hello_kern.c文件
#include <linux/ptrace.h>
#include <linux/version.h>
#include <uapi/linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include "trace_common.h"


SEC("kprobe/" SYSCALL(sys_write))
int bpf_prog(struct pt_regs *ctx) {
  char msg[] = "hello world!\n";
  bpf_trace_printk(msg, sizeof(msg));
  return 0;
}

char _license[] SEC("license") = "GPL";
7.2 第二步,编写hello_user.c文件
#include <bpf/bpf.h>
#include <bpf/libbpf.h>
#include <linux/bpf.h>
#include <fcntl.h>
 
#define DEBUGFS "/sys/kernel/debug/tracing/"
 
void read_trace_pipe(void)
{
        int trace_fd;
 
        trace_fd = open(DEBUGFS "trace_pipe", O_RDONLY, 0);
        if (trace_fd < 0)
                return;
 
        while (1) {
                static char buf[4096];
                ssize_t sz;
 
                sz = read(trace_fd, buf, sizeof(buf) - 1);
                if (sz > 0) {
                        buf[sz] = 0;
                        puts(buf);
                }
        }
}
 
 
int load_bpf_file(const char* object_name) {
	struct bpf_object *objs;
	struct bpf_program *prog;
	struct bpf_link *link = NULL;
	printf("%s\n", object_name);
 
	objs = bpf_object__open_file(object_name, NULL);
 
	if (libbpf_get_error(objs)) {
		fprintf(stderr, "open object file error!\n");
		goto cleanup;
		return -1;
	}
 
	prog = bpf_object__find_program_by_name(objs, "bpf_prog");
	if (!prog) {
		fprintf(stderr, "ERROR: finding a prog in obj file failed\n");
		goto cleanup;
		return -1;
	}
 
	if (bpf_object__load(objs)) {
		fprintf(stderr, "load object file error!\n");
		goto cleanup;
		return -1;
	}
 
	link = bpf_program__attach(prog);
	if (libbpf_get_error(link)) {
		fprintf(stderr, "ERROR: bpf_program__attach failed\n");
		goto cleanup;
		return -1;
	}
 
	return 0;
 
cleanup:
	bpf_link__destroy(link);	
	bpf_object__close(objs);
 
 
	return -1;
	
}
 
int main() {
	if (load_bpf_file("/usr/src/linux-5.13/samples/bpf/hello_kern.o")) {
		return -1;
	}
	read_trace_pipe();
	return 0;
}
7.3 第三步,修改Makefile文件

我们修改Makefile时只需要修改三处,分别在对应的位置添加tprogs-y += hello、hello-objs := hello_user.o、always-y += hello_kern.o,修改后的完整的Makefile文件如下:

tprogs-y += xdp_sample_pkts
tprogs-y += ibumad
tprogs-y += hbm
tprogs-y += hello

xdp_sample_pkts-objs := xdp_sample_pkts_user.o
ibumad-objs := ibumad_user.o
hbm-objs := hbm.o $(CGROUP_HELPERS)
hello-objs := hello_user.o

always-y += hbm_out_kern.o
always-y += hbm_edt_kern.o
always-y += xdpsock_kern.o
always-y += hello_kern.o
7.4 运行eBPF程序并查看结果

在源代码根目录下执行make M=samples/bpf,在samples/bpf/文件夹中查看自己的bpf程序生成的可执行文件。

image.png

执行此eBPF程序

idle@linux:/usr/src/linux-5.13/samples/bpf$ sudo ./hello

image.png

至此ubuntu20.04 eBPF纯C程序的环境搭建与示例操作完毕,后续可以开始深入理解eBPF的原理与应用了。