Ubuntu22.04eBPF深入解析与应用
在Ubuntu 22.04上深入解析eBPF技术,从最初的BPF(Berkley Packet Filter)演变而来,eBPF(extended BPF)已成为一套通用的执行引擎,广泛应用于网络分析、性能分析、系统追踪和网络优化等领域。本文详细介绍了eBPF的基本概念、技术架构以及在Ubuntu 22.04上的环境搭建和样例编写过程。通过实际操作,读者可以了解如何在Ubuntu系统中编译和运行eBPF程序,掌握其在内核和用户空间的交互机制。
我早前接触eBPF技术时,对其用途和解决的问题一头雾水,因此未能深入研究。幸运的是,近期我有机会深入探讨这一技术。
什么是BPF?BPF,即Berkley Packet Filter(伯克利报文过滤器),其设计灵感来源于1992年Steven McCanne和Van Jacobson撰写的论文《The BSD packet filter: A New architecture for user-level packet capture》(《BSD数据包过滤器:一种用于用户级数据包捕获的新体系结构》)。最初,BPF是在BSD内核中实现的,后因其卓越的设计理念,被其他操作系统如Linux所采用。
该论文中,作者描述了他们在Unix内核中实现网络数据包过滤的方法,这种新技术比当时最先进的数据包过滤技术快20倍。下图来自论文:
简而言之,BPF作为网络报文传输的旁路链路,当接收到的网络报文到达内核驱动程序后,报文在传输给网络协议栈的同时,会将其副本传输给BPF。之后,报文会通过BPF程序的内部逻辑进行过滤,最终再送到用户程序。
BPF在数据包过滤上引入了两大创新:
- 一个新的虚拟机(VM)设计,能够有效地在基于寄存器结构的CPU上运行;
- 应用程序使用缓存只复制与过滤数据包相关的数据,不会复制数据包的所有信息,最大程度地减少BPF处理的数据,提高处理效率。我们熟悉的tcpdump就是基于BPF技术,堪称一个神器。
什么是eBPF?BPF发展至今,已升级为eBPF,即「extended Berkeley Packet Filter」。它演变成了一套通用执行引擎,提供基于系统或程序事件高效安全执行特定代码的能力,不再局限于内核开发者。其应用场景扩展到网络分析、性能分析、系统追踪、网络优化等多种类型的工具和平台。
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 -version Ubuntu 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 -version Ubuntu clang version 14.0.0-1ubuntu1 Target: x86_64-pc-linux-gnu Thread model: posix InstalledDir: /usr/bin #
查看当前ubuntu的内核版本,安装对应的内核源码,并解压源码:
# apt-cache search linux-source linux-source - Linux kernel source with Ubuntu patches linux-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 <stdio.h> #include <unistd.h> #include <bpf> #include "trace_helpers.h" <p>int main(int ac, char *<em>argv) { struct bpf_link </em>link = NULL; struct bpf_program <em>prog; struct bpf_object </em>obj; char filename[256];</p><pre class="brush:php;toolbar:false"><code>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 failed\n"); return 0; } prog = bpf_object__find_program_by_name(obj, "bpf_prog"); if (!prog) { fprintf(stderr, "ERROR: finding a prog in obj file failed\n"); goto cleanup; } /* load BPF program */ if (bpf_object__load(obj)) { fprintf(stderr, "ERROR: loading BPF object file failed\n"); goto cleanup; } link = bpf_program__attach(prog); if (libbpf_get_error(link)) { fprintf(stderr, "ERROR: bpf_program__attach failed\n"); link = NULL; goto cleanup; } read_trace_pipe();</code>
cleanup: bpf_link__destroy(link); bpf_object__close(obj); return 0; }
youyeetoo_kern.c的内容:
#include <uapi><h1>include <linux></h1><h1>include <bpf></h1><h1>include <bpf></h1><p>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; }</p><p>char _license[] SEC("license") = "GPL"; u32 _version SEC("version") = LINUX_VERSION_CODE; </bpf></bpf></linux></uapi></p>
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<h1>Libbpf dependencies</h1><p>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)</p><h1>Tell kbuild to always build the programs</h1><p>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</p><p>ifeq ($(ARCH), arm)</p><h1>Strip all except -D<strong>LINUX_ARM_ARCH</strong> option needed to handle linux</h1>
eBPF样例验证编译样例:
# make M=samples/bpf<p>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.o WARNING: Symbol version dump "Module.symvers" is missing. Modules may not have dependencies or modversions. You may get many unresolved symbol warnings.</p>
在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:
在终端1中运行youyeetoo可执行文件,在终端2中执行任意命令,在终端1查看程序是否能够监测到,如果成功监测到新进程运行便会输出一条“bpf_trace_printk: Hello”
好了,本文到此结束,带大家了解了《Ubuntu22.04eBPF深入解析与应用》,希望本文对你有所帮助!关注golang学习网公众号,给大家分享更多文章知识!

- 上一篇
- DebianGolang日志查看及存储路径指南

- 下一篇
- 用JavaScript实现组件生命周期的最佳实践
-
- 文章 · linux | 1天前 |
- Linux防止文件被误删?这个小技巧帮你找回
- 370浏览 收藏
-
- 文章 · linux | 1天前 |
- GitLab项目管理教程:手把手教你用Linux高效管理项目
- 194浏览 收藏
-
- 文章 · linux | 1天前 |
- linux进阶必看!手把手教你用ulimit命令限制进程数量
- 183浏览 收藏
-
- 文章 · linux | 1天前 |
- Hadoop在Linux下网络配置超详细教程分享
- 312浏览 收藏
-
- 文章 · linux | 1天前 |
- GitLab服务器最强插件&工具推荐(Linux版)
- 210浏览 收藏
-
- 文章 · linux | 1天前 |
- 手把手教你检测copendir是否成功?超简单实用技巧!
- 204浏览 收藏
-
- 文章 · linux | 1天前 |
- Linux服务器搞JS日志管理?老司机来分享实用小技巧了
- 245浏览 收藏
-
- 文章 · linux | 1天前 |
- 手把手教你用journalctl查看Linux系统日志
- 419浏览 收藏
-
- 文章 · linux | 1天前 |
- PyTorch能用哪些GPU?Linux玩家速来!
- 242浏览 收藏
-
- 文章 · linux | 1天前 |
- Linux配置静态IP,手把手教学!轻松搞定网络文件路径
- 200浏览 收藏
-
- 文章 · linux | 1天前 |
- Linuxreaddir怎么实现文件排序?手把手教你轻松搞定
- 481浏览 收藏
-
- 文章 · linux | 1天前 |
- Hadoop在Linux如何实现资源管理?深度解读资源调度机制
- 244浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 508次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 484次学习
-
- 茅茅虫AIGC检测
- 茅茅虫AIGC检测,湖南茅茅虫科技有限公司倾力打造,运用NLP技术精准识别AI生成文本,提供论文、专著等学术文本的AIGC检测服务。支持多种格式,生成可视化报告,保障您的学术诚信和内容质量。
- 9次使用
-
- 赛林匹克平台(Challympics)
- 探索赛林匹克平台Challympics,一个聚焦人工智能、算力算法、量子计算等前沿技术的赛事聚合平台。连接产学研用,助力科技创新与产业升级。
- 46次使用
-
- 笔格AIPPT
- SEO 笔格AIPPT是135编辑器推出的AI智能PPT制作平台,依托DeepSeek大模型,实现智能大纲生成、一键PPT生成、AI文字优化、图像生成等功能。免费试用,提升PPT制作效率,适用于商务演示、教育培训等多种场景。
- 53次使用
-
- 稿定PPT
- 告别PPT制作难题!稿定PPT提供海量模板、AI智能生成、在线协作,助您轻松制作专业演示文稿。职场办公、教育学习、企业服务全覆盖,降本增效,释放创意!
- 48次使用
-
- Suno苏诺中文版
- 探索Suno苏诺中文版,一款颠覆传统音乐创作的AI平台。无需专业技能,轻松创作个性化音乐。智能词曲生成、风格迁移、海量音效,释放您的音乐灵感!
- 54次使用
-
- 命令行工具:应对Linux服务器安全挑战的利器
- 2023-10-04 501浏览
-
- 如何使用Docker进行容器的水平伸缩和负载均衡
- 2023-11-07 501浏览
-
- linux .profile的作用是什么
- 2024-04-07 501浏览
-
- 如何解决s权限位引发postfix及crontab异常
- 2024-11-21 501浏览
-
- 如何通过脚本自动化Linux上的K8S安装
- 2025-02-17 501浏览