0

0

基于ubuntu22.04-深入浅出 eBPF

爱谁谁

爱谁谁

发布时间:2025-04-18 11:22:01

|

381人浏览过

|

来源于php中文网

原创

我早前接触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所采用。

基于ubuntu22.04-深入浅出 eBPF该论文中,作者描述了他们在Unix内核中实现网络数据包过滤的方法,这种新技术比当时最先进的数据包过滤技术快20倍。下图来自论文:

基于ubuntu22.04-深入浅出 eBPF简而言之,BPF作为网络报文传输的旁路链路,当接收到的网络报文到达内核驱动程序后,报文在传输给网络协议栈的同时,会将其副本传输给BPF。之后,报文会通过BPF程序的内部逻辑进行过滤,最终再送到用户程序。

BPF在数据包过滤上引入了两大创新:

  1. 一个新的虚拟机(VM)设计,能够有效地在基于寄存器结构的CPU上运行;
  2. 应用程序使用缓存只复制与过滤数据包相关的数据,不会复制数据包的所有信息,最大程度地减少BPF处理的数据,提高处理效率。我们熟悉的tcpdump就是基于BPF技术,堪称一个神器。

什么是eBPF?BPF发展至今,已升级为eBPF,即「extended Berkeley Packet Filter」。它演变成了一套通用执行引擎,提供基于系统或程序事件高效安全执行特定代码的能力,不再局限于内核开发者。其应用场景扩展到网络分析、性能分析、系统追踪、网络优化等多种类型的工具和平台。

基于ubuntu22.04-深入浅出 eBPFeBPF技术架构图:

基于ubuntu22.04-深入浅出 eBPFeBPF主要分为用户空间程序与内核程序两部分:

在用户空间,程序通过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的内核版本,安装对应的内核源码,并解压源码:

PHP 网络编程技术与实例(曹衍龙)
PHP 网络编程技术与实例(曹衍龙)

PHP网络编程技术详解由浅入深,全面、系统地介绍了PHP开发技术,并提供了大量实例,供读者实战演练。另外,笔者专门为本书录制了相应的配套教学视频,以帮助读者更好地学习本书内容。这些视频和书中的实例源代码一起收录于配书光盘中。本书共分4篇。第1篇是PHP准备篇,介绍了PHP的优势、开发环境及安装;第2篇是PHP基础篇,介绍了PHP中的常量与变量、运算符与表达式、流程控制以及函数;第3篇是进阶篇,介绍

下载
# 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 
#include 
#include 
#include "trace_helpers.h"

int 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 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();

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 -DLINUX_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.o WARNING: 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:

基于ubuntu22.04-深入浅出 eBPF在终端1中运行youyeetoo可执行文件,在终端2中执行任意命令,在终端1查看程序是否能够监测到,如果成功监测到新进程运行便会输出一条“bpf_trace_printk: Hello”

基于ubuntu22.04-深入浅出 eBPF

相关专题

更多
C语言变量命名
C语言变量命名

c语言变量名规则是:1、变量名以英文字母开头;2、变量名中的字母是区分大小写的;3、变量名不能是关键字;4、变量名中不能包含空格、标点符号和类型说明符。php中文网还提供c语言变量的相关下载、相关课程等内容,供大家免费下载使用。

400

2023.06.20

c语言入门自学零基础
c语言入门自学零基础

C语言是当代人学习及生活中的必备基础知识,应用十分广泛,本专题为大家c语言入门自学零基础的相关文章,以及相关课程,感兴趣的朋友千万不要错过了。

618

2023.07.25

c语言运算符的优先级顺序
c语言运算符的优先级顺序

c语言运算符的优先级顺序是括号运算符 > 一元运算符 > 算术运算符 > 移位运算符 > 关系运算符 > 位运算符 > 逻辑运算符 > 赋值运算符 > 逗号运算符。本专题为大家提供c语言运算符相关的各种文章、以及下载和课程。

354

2023.08.02

c语言数据结构
c语言数据结构

数据结构是指将数据按照一定的方式组织和存储的方法。它是计算机科学中的重要概念,用来描述和解决实际问题中的数据组织和处理问题。数据结构可以分为线性结构和非线性结构。线性结构包括数组、链表、堆栈和队列等,而非线性结构包括树和图等。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

259

2023.08.09

c语言random函数用法
c语言random函数用法

c语言random函数用法:1、random.random,随机生成(0,1)之间的浮点数;2、random.randint,随机生成在范围之内的整数,两个参数分别表示上限和下限;3、random.randrange,在指定范围内,按指定基数递增的集合中获得一个随机数;4、random.choice,从序列中随机抽选一个数;5、random.shuffle,随机排序。

602

2023.09.05

c语言const用法
c语言const用法

const是关键字,可以用于声明常量、函数参数中的const修饰符、const修饰函数返回值、const修饰指针。详细介绍:1、声明常量,const关键字可用于声明常量,常量的值在程序运行期间不可修改,常量可以是基本数据类型,如整数、浮点数、字符等,也可是自定义的数据类型;2、函数参数中的const修饰符,const关键字可用于函数的参数中,表示该参数在函数内部不可修改等等。

527

2023.09.20

c语言get函数的用法
c语言get函数的用法

get函数是一个用于从输入流中获取字符的函数。可以从键盘、文件或其他输入设备中读取字符,并将其存储在指定的变量中。本文介绍了get函数的用法以及一些相关的注意事项。希望这篇文章能够帮助你更好地理解和使用get函数 。

642

2023.09.20

c数组初始化的方法
c数组初始化的方法

c语言数组初始化的方法有直接赋值法、不完全初始化法、省略数组长度法和二维数组初始化法。详细介绍:1、直接赋值法,这种方法可以直接将数组的值进行初始化;2、不完全初始化法,。这种方法可以在一定程度上节省内存空间;3、省略数组长度法,这种方法可以让编译器自动计算数组的长度;4、二维数组初始化法等等。

602

2023.09.22

c++空格相关教程合集
c++空格相关教程合集

本专题整合了c++空格相关教程,阅读专题下面的文章了解更多详细内容。

0

2026.01.23

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PostgreSQL 教程
PostgreSQL 教程

共48课时 | 7.7万人学习

Git 教程
Git 教程

共21课时 | 2.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号