笔者在很早之前就看ebpf这类似的文章,那时候看这个技术一脸懵逼,不知道它是用来做什么,可以解决什么问题。所以也没有太关注这个技术。很庆幸最近刚好有机会研究这个技术。
什么是bpf 「bpf的全称是berkaley packet filter,即伯克利报文过法器,它的设计思想来源于 1992 年steven mccanne和van jacobson写的一篇论文“the bsd packet filter. a new architecture for user-level packet apture' (《bsd数据包过滤器:一种用于用户级数据包捕获的新休系结构》)。最初,bpf是在 bsd 内核实现的,后来,由于其出色的设计思想,其他操作系统也将其引入包括 linux。」
「在这篇论文中,作者描述了他们如何在unix内核实现网络数据包过滤,这种新的技术比当时最先进的数据包过滤技术快20倍。如下图来源于论文:」
「通俗易懂的理解上图,bpf是作为网络报文传输的旁路链路,当接收到的网络报文到达内阁驱动程序后,网络报文在传输给网络协议栈的同时,会额外将网络报文的副本传输给bpf。之后网络报文会经过bpf程序的内部逻辑进行过滤,最终再送到用户程序。」
「bpf 在数据包过滤上引入了两大革新:」
一个新的虚拟机(vm)设计,可以有效地工作在基于寄存器结构的cpu之上; 应用程序使用缓存只复制与过滤数据包相关的数据,不会复制数据包的所有信息,最大程度地减少bpf处理的数据,提高处理效率。 「我们熟悉的tcpdump就是基于bpf技术,好比一个神器站另外一个神器的绝作。」
什么是ebpf bpf发展到现在名称升级为ebpf: 「extended berkeley packet filter」。它演进成为了一套通用执行引擎,提供可基于系统或程序事件高效安全执行特定代码的通用能力,通用能力的使用者不再局限于内核开发者。其使用场景不再仅仅是网络分析,可以基于ebpf开发性能分析、系统追踪、网络优化等多种类型的工具和平台。
ebpf原理 ** ebpf技术架构图:**
ebpf主要分为用户空间程序与内核程序两大部分:
在用户空间,程序通过llvm/clang被编译成ebpf可接受的字节码并提交到内核,以及负责读取内核回传的消息事件或统计信息。ebpf提供了两种内核态与用户态传递数据的方式,内核态可以将自定义perf_event消息事件发往用户态,或用户态通过文件描述符读写存储在内核中的k/v map数据。
在内核空间,为了稳定与安全,ebpf接收的字节码首先会交给verifier进行安全验证,如验证程序循环次数,数组越界问题,无法访问的指令等等。只有校验通过的字节码才会提交到内核自带编译器或jit编译器编译成可直接执行的机器指令。同时,ebpf对提交程序提出限制,如程序大小限制,最大可使用堆栈大小限制,可调用函数限制,循环次数限制等。
从上面的架构图可以看出,ebpf在内核态会依赖内核探针进行工作,其中kprobes实现内核函数动态跟踪;uprobes实现用户函数动态跟踪;tracepoints是内核中的静态跟踪点;perf_events支持定时采样和pmc。
ebpf环境搭建 为了有一个ebpf程序编写验证的平台,我在ubuntu22.04中搭建了ebpf环境,ubuntu22.04安装流程在这里不在过多的介绍。「以下的操作都在root用户下执行」
更新系统的包索引和包列表: # apt update 编译 bpf 程序需要系统安装必备的 linux-headers 包: # sudo apt install linux-headers-$(uname -r) 安装ebpf依赖工具: # apt install -y bison flex build-essential git cmake make libelf-dev strace tar libfl-dev libssl-dev libedit-dev zlib1g-dev python python3-distutils 安装llvm,并检查一下版本: # apt install llvm# llc -versionubuntu llvm version 14.0.0 ..... wasm32 - webassembly 32-bit wasm64 - webassembly 64-bit x86 - 32-bit x86: pentium-pro and above x86-64 - 64-bit x86: em64t and amd64 xcore - xcore# 安装clang,并检查一下版本: # apt install clang# clang -versionubuntu clang version 14.0.0-1ubuntu1target: x86_64-pc-linux-gnuthread model: posixinstalleddir: /usr/bin# 查看但钱ubuntu的内核版本,安装对应的内核源码,并解压源码: # apt-cache search linux-sourcelinux-source - linux kernel source with ubuntu patcheslinux-source-5.19.0 - linux kernel source for version 5.19.0 with ubuntu patches# apt install linux-source-5.19.0# cd /usr/src# tar -jxvf linux-source-5.19.0.tar.bz2# cd linux-source-5.19.0 编译内核源码的bpf模块,如果没有报错,说明已经完成环境搭建: # cp -v /boot/config-$(uname -r) .config# make oldconfig && make prepare# make headers_install# apt-get install libcap-dev # make m=samples/bpf cc samples/bpf/../../tools/testing/selftests/bpf/cgroup_helpers.o cc samples/bpf/../../tools/testing/selftests/bpf/trace_helpers.o cc samples/bpf/cookie_uid_helper_example.o cc samples/bpf/cpustat_user.o cc samples/bpf/fds_example.o....warning: symbol version dump module.symvers is missing. modules may not have dependencies or modversions. you may get many unresolved symbol warnings. ebpf样例编写 在内核源码的samples/bpf目录下提供了很多实例供我们学习,通过目录下的makefile就可以构建里面的bpf程序,如果我们用 c 语言编写的 bpf 程序编译可以直接在该目录提供的环境中进行编译。
samples/bpf 下的程序一般组成方式是 xxx_user.c 和 xxx_kern.c:
xxx_user.c:为用户空间的程序用于设置 bpf 程序的相关配置、加载 bpf 程序至内核、设置 bpf 程序中的 map 值和读取 bpf 程序运行过程中发送至用户空间的消息等。目前 xxx_user.c 与 xxx_kern.c 程序在交互实现都是基于 bpf() 系统调用完成的。直接使用 bpf() 系统调用涉及的参数和细节比较多,使用门槛较高,因此为了方便用户空间程序更加易用,内核提供了 libbpf 库封装了对于 bpf() 系统调用的细节。 xxx_kern.c:为 bpf 程序代码,通过 clang 编译成字节码加载至内核中,在对应事件触发的时候运行,可以接受用户空间程序发送的各种数据,并将运行时产生的数据发送至用户空间程序。 编写一个样例流程,在目录samples/bpf中新建两个文件:youyeetoo_user.c和youyeetoo_kern.c,并且在makefile中加入构建:
youyeetoo_user.c的内容: #include #include #include #include trace_helpers.hint main(int ac, char **argv){ struct bpf_link *link = null; struct bpf_program *prog; struct bpf_object *obj; char filename[256]; snprintf(filename, sizeof(filename), %s_kern.o, argv[0]); obj = bpf_object__open_file(filename, null); if (libbpf_get_error(obj)) { fprintf(stderr, error: opening bpf object file failedn); return 0; } prog = bpf_object__find_program_by_name(obj, bpf_prog); if (!prog) { fprintf(stderr, error: finding a prog in obj file failedn); goto cleanup; } /* load bpf program */ if (bpf_object__load(obj)) { fprintf(stderr, error: loading bpf object file failedn); goto cleanup; } link = bpf_program__attach(prog); if (libbpf_get_error(link)) { fprintf(stderr, error: bpf_program__attach failedn); link = null; goto cleanup; } read_trace_pipe();cleanup: bpf_link__destroy(link); bpf_object__close(obj); return 0;} youyeetoo_kern.c的内容: #include #include #include #include sec(tracepoint/syscalls/sys_enter_execve)int bpf_prog1(struct pt_regs *ctx){ char fmt[] = youyeetoo %s !n; char comm[16]; bpf_get_current_comm(&comm, sizeof(comm)); bpf_trace_printk(fmt, sizeof(fmt), comm); return 0;}char _license[] sec(license) = gpl;u32 _version sec(version) = linux_version_code; makefile 文件修改: # diff -u makefile.old makefile--- makefile.old 2021-09-26 03:16:16.883348130 +0000+++ makefile 2021-09-26 03:20:46.732277872 +0000@@ -55,6 +55,7 @@ tprogs-y += xdp_sample_pkts tprogs-y += ibumad tprogs-y += hbm+tprogs-y += youyeetoo # libbpf dependencies libbpf = $(tools_path)/lib/bpf/libbpf.a@@ -113,6 +114,7 @@ xdp_sample_pkts-objs := xdp_sample_pkts_user.o ibumad-objs := ibumad_user.o hbm-objs := hbm.o $(cgroup_helpers)+youyeetoo-objs := youyeetoo_user.o $(trace_helpers) # tell kbuild to always build the programs always-y := $(tprogs-y)@@ -174,6 +176,7 @@ always-y += hbm_out_kern.o always-y += hbm_edt_kern.o always-y += xdpsock_kern.o+always-y += youyeetoo_kern.o ifeq ($(arch), arm) # strip all except -d__linux_arm_arch__ option needed to handle linux ebpf样例验证 编译样例: # make m=samples/bpf cc samples/bpf/../../tools/testing/selftests/bpf/cgroup_helpers.o cc samples/bpf/../../tools/testing/selftests/bpf/trace_helpers.o cc samples/bpf/cookie_uid_helper_example.o cc samples/bpf/cpustat_user.o cc samples/bpf/fds_example.o.... ld samples/bpf/youyeetoo clang-bpf samples/bpf/youyeetoo_kern.owarning: symbol version dump module.symvers is missing. modules may not have dependencies or modversions. you may get many unresolved symbol warnings. 在samples/bpf下查看编译结果,可以看到youyeetoo可执行文件: # ls -al youyeetoo*-rwxr-xr-x 1 root root 407976 6月 9 19:08 youyeetoo-rw-r--r-- 1 root root 451 6月 9 10:44 youyeetoo_kern.c-rw-r--r-- 1 root root 5216 6月 9 19:08 youyeetoo_kern.o-rw-r--r-- 1 root root 997 6月 9 10:40 youyeetoo_user.c-rw-r--r-- 1 root root 3360 6月 9 19:08 youyeetoo_user.o 在ubuntu中运行两个终端,用来测试youyeetoo: 在终端以运行youyeetoo可执行文件,在终端2中执行任意命令,在终端1查看程序是否能够监测到,如果成功监测到新进程运行便会输出一条“bpf_trace_printk: hello”
原文标题:基于ubuntu22.04-深入浅出 ebpf
文章出处:【微信公众号:rice 嵌入式开发技术分享】欢迎添加关注!文章转载请注明出处。
TMC2209-LA电机驱动器IC的概念、特征及应用
基于全数字公共照明系统中组群控制器的原理及设计方案
“海康微影”最新推出专业型的手持测温热像仪H13
ZUK Edge会是ZUK品牌的最后一款手机?
罗技G431/G331耳机试玩 助力草根玩家逆袭翻盘
基于ubuntu22.04-深入浅出 eBPF
励磁同步电机的工作过程和使用注意事项
森源集团提供智慧能源一站式电力系统解决方案
半导体制造系统自动湿蚀站调度的优化方法
不限汽车!TI、Acconeer、英飞凌加速布局60GHz毫米波雷达消费品市场
华为Mate9怎么样?华为Mate9开箱评测:18个月不卡顿?颜值逆天,5个月使用感受足够你下决心购买
DVB-T解调器的数据传输和前FFT同步系统的设计
中国半导体行业一片生机勃勃,却有一颗不自主的“芯”
Omdia预计:400G在整个通信网络中将加速发展
中美芯片冲突拉开新战线,出货有望飙升至100倍
光伏变压器做无功补偿的好处
WiFi6到底是什么,它和传统WiFi有什么区别
中国集成电路产业2018年前三季度的情况分析
Oslux S.2.1多合一芯片LED为相机闪光灯应用提供全新 LED 版本
新闻 暴风股价开盘跌停 暴风市值崩到20亿 网易游戏裁员