系列综述:
💞目的:本系列是个人整理为了学习ebpf机制
的,整理期间苛求每个知识点,平衡理解简易度与深入程度。
🥰来源:材料主要源于–知乎ebpf专栏文章
–进行的,每个知识点的修正和深入主要参考各平台大佬的文章,其中也可能含有少量的个人实验自证。
🤭结语:如果有帮到你的地方,就点个赞和关注一下呗,谢谢🎈🎄🌷!!!
请先收藏!!!,后续继续完善和扩充👍(●’◡’●)
文章目录
- 一、eBPF介绍
- 简介
- eBPF特性
- 二、eBPF程序
- 基础
- eBPF程序生命周期
- 三、SEC程序类型(未完待续)
- SEC概述
- XDP类型程序
- 用户与内核的交互
- 参考博客
😊点此到文末惊喜↩︎
前言
- eBPF提升内核的能效是目标,它要发挥的是瑞士军刀的作用,如果只是为了eBPF而eBPF,那它就是狗皮膏药,你只是为了把它贴到身体的某个地方而已,并且你也会一直找这样的地方,找准机会就贴上去。另一句意思相同的话,大概是如果你有个锤子,那么眼里什么都是钉子
- eBPF应该慎用,其会增加系统的复杂度,从而导致故障概率的提升
一、eBPF介绍
简介
- eBPF概述
- 背景:eBPF(extended Berkeley Packet Filter)是一种扩展的伯克利包过滤器,由原来的网络过滤器BPF 演进成的一个可插拔可编程的内核模块eBPF,可基于此开发性能分析工具、软件定义网络等诸多场景。
- 定义:处于内核中的一个高效与灵活的虚拟机组件,以一种安全的方式在许多内核 hook 点执行字节码。
- 作用
- 内核可编程:eBPF不需要重新编译内核,并实现动态注入内核运行并随时卸载
- 嵌入内核:直接与内核进行交互,避免了用户态和内核态的切换开销
- 内核监控:追踪内核中的各种事件或资源使用情况,从而进行调试或性能优化
- 安全加载:通过Verifier验证器和其他安全检查,以及helper API的能力限制,实现安全的加载到内核
- 功能直达:绕过内核中非必要的子系统,直接进行数据包的处理和转发
- 特点
- bpf_helpers:eBPF 程序不能调用任意的内核参数,只限于内核模块中列出的 BPF Helper API函数
- 有界循环:eBPF 程序中循环次数限制且必须在有限时间内结束,防止系统运行出现死锁
- 一旦进入内核,eBPF便拥有了上帝视角,既可以监控内核,也可以管窥用户态程序。并且eBPF技术提供的一系列工具(Verifier)可以检测eBPF的代码安全,避免恶意程序进入到内核态中执行。
- eBPF的组成
- 用户态程序:负责加载 BPF 字节码至内核,如需要也会负责读取内核回传的统计信息或者事件详情
- 内核态BPF字节码程序: 负责在内核中执行特定事件,如需要也会将执行的结果通过 maps 或者 perf-event 事件发送至用户空间
- 其中用户空间程序与内核 BPF 字节码程序可以使用
map 结构
实现双向通信
- eBPF的执行流程
- 用户态
- 编译:使用 LLVM 或 GCC 工具将编写的
eBPF 程序
编译成eBPF 字节码
- 加载:调用 bpf()系统调用把 eBPF 字节码加载到内核。
- 编译:使用 LLVM 或 GCC 工具将编写的
- 内核态
- Verfier验证与JIT编译:内核使用
验证器
验证字节码安全性,通过JIT(Just In Time)将 eBPF 字节码编译成机器码(Native Code) - Kernel Helper API:内核会根据eBPF功能将机器码挂载到内核对应的Hook,当运行到某个Hook就会执行对应的eBPF程序
- Verfier验证与JIT编译:内核使用
- 用户态
- eBPF中用户态与内核态的通信方式
- maps方式:将
数据
存储在内核键值对
数据结构中,数据由内核eBPF字节码程序更新,并共享给用户态程序进行周期性查询 - perf-event方式:可用于捕获内核中的多种
软硬件事件
,并将这些事件实时
发送给用户空间程序
- maps方式:将
- eBPF应用实践
- cilium: 将eBPF技术应用于Kubernetes中,提供高性能网络和安全功能。
- Falco: 基于eBPF技术的云原生安全运行时,用于在Kubernetes环境中检测威胁。
- Katran: 使用eBPF实现的高性能四层负载均衡器,用于处理网络流量。
- pixie: 一种可观察性工具,使用eBPF技术提供对Kubernetes应用程序的详细监控和调试能力。
eBPF特性
- eBPF中的Hook
- 定义:hook是指内核中特定函数(事件)的发生,包括系统调用、函数进入/退出、网络事件等
- 原理:
- tracepoint机制:基于
内核标记点tracepoint
来追踪内核中的特定事件 - kprobes机制:可以在
内核函数的入口或出口
处插入Hook - uprobes机制:可以在
用户空间函数的入口或出口
处插入Hook
- tracepoint机制:基于
- eBPF的Verification
- 过程:每一个 eBPF 程序加载到内核前,都要经过 Verification来保证 eBPF 程序的安全性
- 加载对象:除非节点开启了
unpriviledged 特性
,否则只有高特权级的程序
才能够加载 eBPF 程序 - 作用:
- 核心:保证 eBPF 程序不会崩溃或者使得系统出故障
- 时间:保证 eBPF 程序不能陷入死循环
- 空间:保证 eBPF 程序必须满足系统要求的大小,过大的 eBPF 程序不允许被加载进内核
- JIT Compilation
- 作用:Just-In-Time(JIT) 编译用来将通用的 eBPF 字节码翻译成与机器相关的指令集,从而极大加速 BPF 程序的执行
- 特点:
- 与解释器相比,可以降低每个指令的开销
- 减少生成的可执行镜像的大小
- 针对CISC 指令集(例如 x86),JIT 做了很多特殊优化
- BPF Map
- 定义:驻留在内核空间中高效的
键值存储结构
,用于实现用户态程序、内核态BPF程序以及其他内核态程序间的通信 - 作用:
- 由于安全原因 BPF 程序不允许访问全局变量,可以使用 map 来充当全局变量
- 通过BPF Tail call实现BPF程序间的跳转,主要是通过map获取其他BPF的程序指针实现的
- 注意
- 多对多:一个map可以共享给多个不同的BPF程序,而一个 BPF 程序也可以访问多个不同的map(目前最多64个)
- 多CPU架构:基于 per-cpu类型,每个CPU在同一个map中的数据被相互隔离,从而实现更高效的查找和聚合操作
- 解耦:eBPF 程序不能够随意调用内核函数,避免 eBPF 程序与特定的内核版本的绑定
- 定义:驻留在内核空间中高效的
- BPF辅助函数
- 所有的 BPF 辅助函数都是核心内核的一部分,无法通过内核模块(kernel module)来扩展或添加。
- BPF辅助函数的参数列表和底层系统调用约定相匹配
- Tail Calls(尾调用机制)
- 使用长跳转(long jump)实现的,复用原来的栈帧 ,调用开销小
- 相同类型的程序才可以尾调用,而且它们还要与 JIT 编译器相匹配
- BPF to BPF calls
- 减小了生成的 BPF 代码大小,因此 对 CPU 指令缓存(instruction cache,i-cache)更友好。
- Object Pinning(钉住对象)
- 定义:用于将eBPF程序的map数据固定在内核指定位置,以便快速访问,从而避免eBPF生命周期结束导致map数据的丢失。
通过下面网站进行完善ebpf基础知识https://developer.aliyun.com/search?k=ebpf&scene=community&page=1
二、eBPF程序
基础
- 基本环境
- eBPF需要Linux 4.9以上,最好5.xx的版本才能较好的支持eBPF特性
- eBPF程序由两部分组成
- 内核态源代码
xxx_bpf_kern.c
:包含 eBPF 程序的实际逻辑 - 用户态辅助代码
xxx_bpf_user.c
:负责加载、运行和与内核BPF程序交互,从而实现用户处理逻辑
- 内核态源代码
- eBPF程序的本质
- BPF目标文件(bpf_program.o)实质上也是一个ELF格式的文件,其中的bpf_prog字段的内容是BPF程序编译后的字节码(byte code))
- eBPF程序的执行流程
- 编写:基于C语言编写成的eBPF程序,如bpf_xxx.c。
- 编译:通过LLVM编译器将 eBPF 程序编译成eBPF 目标文件(BPF 字节码及符号表等其他元数据),如bpf_function_name.o。
- 验证:使用内核的验证器对 eBPF 目标文件进行安全性和正确性验证,确保不会对内核造成不良影响。这一步骤会检查程序是否存在非法内存访问、循环是否会导致死循环等问题。
- JIT 编译(可选):如果内核支持即时编译(JIT),可以将 BPF 字节码编译成本地机器码,以提高执行效率。JIT 编译可以根据具体的硬件架构进行优化,使得程序执行更快。
- 加载:通过 BPF 的相关库或工具将 BPF 程序加载到内核。加载过程中,会根据程序的类型和目的,将其关联到特定的内核事件或数据结构上。
- 执行:当满足特定的触发条件时,内核执行 BPF 程序。例如,对于跟踪系统调用的 BPF 程序,当有系统调用发生时,程序会被触发执行。
- 交互:BPF 程序可以通过 BPF 映射与用户空间程序进行交互。用户空间程序可以读取和修改映射中的数据,从而实现对 BPF 程序的控制和获取运行状态信息。
- eBPF程序以字节码形式加载到内核的优点
- 安全性:字节码是一种中间表示形式,方便在加载到内核之前进行验证和审查
- 跨平台性:字节码是一种与平台无关的格式,可以在不同的架构和操作系统上运行
- 灵活性:字节码程序可以在无需重新编译内核的情况下,通过
动态链接
在内核运行时进行加载或卸载 - 高性能:虽然eBPF程序是以字节码的形式加载到内核,但在执行时可以被即时编译(JIT)成机器码,以获得最佳的性能。
- 可维护性:字节码格式使得eBPF程序更容易维护和调试。开发者可以使用工具来分析和优化字节码,从而提高程序的效率和可靠性
- eBPF的可移植性
- 原因:Linux内核在快速演进中,依赖于内核数据结构的BPF程序可能因版本更新而出现无效依赖问题
- 解决
- BTF(BPF Type Format):提供结构信息以避免对Clang和内核头文件的依赖
- CO-RE(Compile Once – Run Everywhere):一次编译到处执行,解决了内核数据结构在不同版本差异导致的兼容性问题。使得编译出的BPF字节码是可重定位(relocatable)的,避免了LLVM重新编译的需要。
- eBPF程序的开发方式
- Go/Rust+libbpf:具有高度的灵活性和性能,libbpf是用于创建BPF用户态程序的内核依赖库,GitHub镜像仓库地址
- Go+cilium/ebpf:可使用高级语言进行开发,并能与容器编排平台的集成。
- Python+BBC:简单易用,适合新手,但是性能差、灵活性差
eBPF程序生命周期
- BPF对象
- 定义:一个响应系统事件并在内核态和用户态间进行数据共享的内核程序抽象,从而实现流量分析和运行时检测和防止恶意行为。
- 类型
- BPF程序(progs):
附加到内核并响应Hook事件的安全小程序
,如kprobe、tracepoint等。 - BPF映射(maps):在内核空间中
存储键值对的数据结构
,用于内核BPF程序和用户态主程序的数据存储和共享 - 调试信息(debug info):与BPF程序相关的调试信息,如日志打印、函数签名和性能监控等
- BPF程序(progs):
用户态程序
通过系统调用bpf
操作内核态BPF对象
- 用于bpf_map:进行map的
增删改查和创建
等 - 用于bpf_prog:进行BPF程序的
加载、附加、执行、固定和分离
等
- 用于bpf_map:进行map的
- BPF对象的创建
- bpf_map的创建:
- 调用接口:用户态程序通过系统调用
bpf(BPF_MAP_CREATE, &attr, sizeof(attr))
来创建一个bpf_map对象 - 分配内存:内核会给
struct bpf_map
对象分配内存,用于存储map的类型、键值和最大条目数等 - 初始化:设置该对象的引用计数
refcnt=1
,表示该对象当前被引用一次 - 返回值:返回一个
文件描述符fd
,以供用户态进程对map的进行访问操作
- 调用接口:用户态程序通过系统调用
- bpf_prog的创建
- 用户空间程序通过系统调用
bpf(BPF_PROG_LOAD, ...)
来加载一个bpf_prog对象 - 校验BPF:将该BPF对象相关联的每个map的
refcnt++
,设置该程序本身的refcnt=1
- JIT编译:eBPF 字节码被转换为底层机器指令,从而提高了程序的执行效率
- attach:将机器指令附着在指定的内核Hook点上下文中
- 返回值:返回一个BPF对象的文件描述符fd
- 用户空间程序通过系统调用
- bpf_map的创建:
- BPF对象的关闭(类似cpp智能指针)
- 基础:BPF对象通过
文件描述符(FD)
进行索引和访问,通过引用计数器
进行生命周期的管理 - 文件描述符(File Descriptor):用户态程序通过
bpf()系统调用
来创建eBPF对象后,该调用会返回一个用于索引对应BPF对象
的文件描述符
- 引用计数器:
- 每个BPF对象都有一个引用计数器(ref_cnt),用于追踪对象被引用的次数,从而使多个不同类型能够访问同一个BPF对象
- BPF对象的引用计数
大于0
,内核将保持其活动状态
。当引用计数等于零
时,内核会释放相关资源
。
- 计数原理
- 初始化为1:当对象被创建时,其引用计数器会被初始化为1。
- 访问则+1:其他进程通过FD访问BPF对象时,该对象的引用计数器会+1
- 访问关闭则-1:若对BPF对象的访问关闭 ,则相应的引用计数器会-1。
- 为0则释放:当引用计数器为0时,内核会在经过grace period(宽限期)后释放对象所占用的内存
- 注意:若引用BPF对象的
进程崩溃
,内核会主动清理
进行该进程相关资源,其中包括了对于其相关联的引用计数器-1操作
- 基础:BPF对象通过
- BPF对象attach到的Hook类型?
- 全局类型Global:通常与
整个系统
活动有关,是内核定义的通用跟踪点- Tracepoints:开发者预先
静态编写在常用系统调用入口或出口
的跟踪点。 - Kprobes:在
动态插入任意内核函数的入口或出口
,性能较差且可能影响内核稳定性
- Tracepoints:开发者预先
- 本地类型Local:通常与
特定的
网络接口、文件描述符或进程相关- cgroup_skb:用于特定 cgroup 范围内的网络流量管理和分析
- XDP:在数据包到达网络驱动并进入内核网络栈之前触发 BPF 程序,用于快速处理网络数据包。
- TC Classifier:在网络流量的入口和出口点进行流量分类和过滤,可以用于实现防火墙规则、流量整形等。
- Socket Filter:在 socket 级别对网络流量进行过滤,可以用于捕获或修改经过特定 socket 的数据包。
- 全局类型Global:通常与
- BPF对象的如何实现持久化运行
- BPF文件系统(BPFFS):BPF对象可以被“固定”(pin)到BPF文件系统中,这样它们就独立于创建它们的用户空间进程。通过将BPF对象固定到BPFFS中的某个路径,可以增加其引用计数,从而使得BPF对象在用户空间进程退出后仍然保持活动状态。当需要解除固定时,只需从BPFFS中删除相应的文件,内核会相应地减少引用计数。
- Attach BPF程序到全局挂钩点:某些BPF程序可以附加到全局挂钩点,如XDP、tc clsact、cgroup-based hooks等。这些全局类型的BPF对象即使在创建它们的用户空间进程退出后,仍然可以保持活动状态,因为它们被设计为全局可访问的。例如,一个XDP程序可以附加到网络设备的ingress或egress qdisc,并且会一直处理数据包,直到显式地从qdisc上删除。
- 特点:
- 上下文依赖:eBPF程序是在特定的内核事件触发时执行的,例如当一个网络数据包到达时,或者一个系统调用被执行时。这些事件为eBPF程序提供了执行的上下文。
- 加载和附加:eBPF程序通常由用户空间程序加载和附加到内核的特定挂钩点上。这个用户空间程序负责设置eBPF程序的执行环境,而不是通过main函数来启动。
- 生命周期管理:eBPF程序的生命周期是由加载它们的用户空间程序管理的。用户空间程序可以控制何时加载、卸载eBPF程序,以及何时将它们附加到内核的特定事件上。
- 沙箱环境:为了安全起见,eBPF程序在内核中运行在一个受限的沙箱环境中。这意味着它们不能像普通用户程序那样执行任意操作,而是只能执行内核允许的操作。
- 与内核交互:eBPF程序通过内核提供的BPF系统调用来与内核交互,例如创建BPF Maps、加载eBPF程序、附加eBPF程序到挂钩点等。
- 事件驱动:eBPF程序是事件驱动的,它们响应内核中的事件,而不是通过一个主循环来驱动程序的执行。
- eBPF程序基本结构
- 基础库文件:
- <linux/bpf.h>:eBPF编程的
必须头文件
,定义了eBPF相关的数据结构和函数原型。 - <bpf/bpf_helpers.h>:用于简化eBPF程序开发的
辅助功能函数
,例如用于内存映射、打印日志等。
- <linux/bpf.h>:eBPF编程的
- 许可协议类型:当eBPF程序的许可证与内核模块的
许可证兼容
时,才能加载和运行该程序。 - eBPF程序类型及代码:
- SEC宏:指定eBPF程序的类型,每种类型对应不同
内核挂载点
(eBPF程序所执行的上下文位置) - BPF中的函数:可以指定多个SEC类型函数,和辅助功能函数
- SEC宏:指定eBPF程序的类型,每种类型对应不同
// 1.eBPF必须的库文件 #include <linux/bpf.h> #include <bpf/bpf_helpers.h> // 2. 许可协议类型 char _license[] SEC("license") = "GPL"; // 3. eBPF程序类型及代码 SEC("socket") int bpf_prog(struct __sk_buff *skb) { bpf_trace_printk("Hello, World!\n"); return 0; }
- 基础库文件:
三、SEC程序类型(未完待续)
SEC概述
- SEC关键字
- 概述:是一个宏定义,定义了
BPF的程序类型
,是 “Section” 的缩写 - 作用
- 对 BPF 程序中的函数或变量进行分组和分类,以便在加载和执行时进行控制。
- 不同的 SEC 关键字可将 BPF 程序挂钩到特定的内核机制上,实现特定的功能。
- 类型
- SEC(“kprobe/”):挂钩到内核的 kprobe,在内核函数入口点执行自定义 BPF 程序
- SEC(“kretprobe/”):挂钩到内核的 kretprobe,在内核函数返回点执行自定义 BPF 程序。
- SEC(“tracepoint/”):挂钩到内核的 tracepoint,跟踪内核预定义事件。
- SEC(“perf_event/”):挂钩到内核性能事件,测量和跟踪系统性能指标。
- SEC(“tracepoint”):指定函数为跟踪点,在 Linux 内核跟踪事件中插入代码。
- SEC(“maps”):指定变量为 BPF 映射,用于在 BPF 程序和用户空间之间共享数据。
- SEC(“license”):指定变量为 BPF 程序许可证,说明使用权限和限制。
- SEC(“xdp_sock”):指定 BPF 程序类型为 XDP 套接字型,挂钩到 Linux 内核的 XDP 处理路径,从而在在网络数据包进入内核协议栈之前进行处理
- 概述:是一个宏定义,定义了
XDP类型程序
- XDP(eXpress Data Path)
- 定义: Linux 内核中的一种高性能可编程数据路径,专为网络接口级的数据包处理而设计。
- 作用:
- 高性能:eBPF 程序在
网络设备驱动程序
的软中断上下文中执行,从而实现高性能
的数据包处理 - 绕过内核:能够在数据包
到达内核网络栈前
进行拦截并处理,相比 iptables等有更大的灵活性和细粒度控制。 - 安全性:eBPF 虚拟机确保用户定义的 XDP 程序是被隔离的,不会对内核造成不稳定影响。
- 高性能:eBPF 程序在
- 应用实践
- Cilium :云原生环境(尤其是 Kubernetes)设计的开源网络、安全和可观测性工具
- Katran:是由 Facebook 开发的负载均衡器,优化了高可扩展性和性能,使用 XDP 处理数据包转发,开销极小。
- Cloudflare:实现了基于 XDP 的实时 DDoS 缓解。通过在 NIC 级别处理数据包,过滤掉攻击流量,最小化 DDoS 攻击的影响。
- cgroup_skb
- 定义:Linux内核中用于控制和限制网络数据包传输的机制,与控制组(cgroup)相关。
- 作用:
- 流量控制:可以限制特定控制组的网络流量。
- 资源分配:确保关键任务的网络资源不被非关键任务过度占用。
- 应用实践
- 容器化环境:在Docker或Kubernetes中,用于限制容器的网络带宽和数据包传输速率。
- fentry
- 定义:Linux内核中的一个函数入口点,用于在函数执行前进行拦截。
- 作用:
- 函数跟踪:允许在函数执行前获取调用信息。
- 性能分析:用于性能分析工具,如perf,以获取函数调用的详细情况。
- 应用实践
- 性能监控:在系统性能调优中,用于监控关键函数的调用次数和时间。
- kprobe
- 定义:Linux内核中用于动态跟踪内核函数执行的工具。
- 作用:
- 函数监控:允许监控内核函数的执行。
- 错误诊断:用于内核错误和性能问题的诊断。
- 应用实践
- 内核调试:在开发和测试阶段,用于调试内核代码。
- kprobe_percpu
- 定义:针对每个CPU核心的kprobe,用于在多核系统中进行函数跟踪。
- 作用:
- 多核优化:确保每个核心的函数调用都能被跟踪。
- 性能分析:用于分析多核系统中的函数调用性能。
- 应用实践
- 高并发系统:在需要精确监控每个核心性能的系统中使用。
- kprobepin
- 定义:一种特殊的kprobe,用于在内核函数执行时进行固定位置的跟踪。
- 作用:
- 精确跟踪:允许在内核函数的特定位置进行跟踪。
- 性能分析:用于分析特定函数调用的性能影响。
- 应用实践
- 性能调优:在需要精确控制跟踪点的位置时使用。
- ringbuffer
- 定义:一种循环缓冲区,用于在生产者和消费者之间传递数据。
- 作用:
- 数据传递:用于在不同组件之间传递数据。
- 性能优化:通过减少锁的使用,提高数据传递的效率。
- 应用实践
- 日志系统:用于内核日志的收集和传输。
- tracepoint in_c
- 定义:跟踪点,用于在内核中跟踪特定事件的发生。
- 作用:
- 事件监控:允许监控内核中的特定事件。
- 性能分析:用于分析内核事件对性能的影响。
- 应用实践
- 内核调试:在开发和测试阶段,用于调试内核事件。
- uretprobe
- 定义:用户态返回探针,用于跟踪用户态函数的返回路径。
- 作用:
- 函数监控:允许监控用户态函数的返回。
- 性能分析:用于分析用户态函数的性能。
- 应用实践
- 用户态程序调试:在开发和测试用户态程序时使用。
用户与内核的交互
- BPF MAP为BPF程序的内核态与用户态提供了一个双向数据交换的通道。
- 由于bpf map存储在内核分配的内存空间,处于内核态,可以被运行于在内核态的多个BPF程序所共享,同样可以作为多个BPF程序交换和共享数据的机制。
- MAP就是BPF中代表抽象数据容器(abstract data container)的一个概念
- 支持多种MAP类型,具体在 【libbpf库中的bpf头文件中】
- BPF_MAP_TYPE_HASH类型是BPF支持的第一种MAP数据结构,这个类型可以理解为我们日常接触的hash映射表,通过键值对的形式索引数据
- bpf其实是一个“富调用”,即不止能干一件事,通过cmd传入的值不同,它可以围绕BPF完成很多事情。最主要的功能是加载bpf程序(cmd=BPF_PROG_LOAD),其次是围绕MAP的一系列操作,包括创建MAP(cmd=BPF_MAP_CREATE)、MAP元素查询(cmd=BPF_MAP_LOOKUP_ELEM)、MAP元素值更新(cmd=BPF_MAP_UPDATE_ELEM)等。当cmd=BPF_MAP_CREATE时,即bpf执行创建MAP的操作后,bpf调用会返回一个文件描述符fd,通过该fd后续可以操作新创建的MAP。通过fd访问map,这个很unix!
- MAP本质上也是由bpf系统调用创建的,bpf程序只需要声明map的key、value、type等组成信息即可。用户态可以通过bpf系统调用返回的fd操作map,libbpf和cilium/ebpf等封装了对fd的操作,这样简化了API的使用。
🚩点此跳转到首行↩︎
参考博客
- cilium ebpf:使用go开发ebpf程序
- Getting Started with eBPF in Go
- x大规模微服务利器:eBPF + Kubernetes 介绍
- xx cilium ebpf:使用go开发ebpf程序
- xxxxxx eBPF 核心技术与实战
- bfp笔记
- eBPF 入门实践教程二十一: 使用 XDP 进行可编程数据包处理
- 聊聊对 BPF 程序至关重要的 vmlinux.h 文件
- !!!!待查看的ebpf程序编译执行方式
- 待学习
- 深入理解 ebpf loader