Go 语言 + libbpfgo 实战 eBPF 开发
Go 语言 + libbpfgo 实战 eBPF 开发
1. 引言
这是专栏的第一篇文章,我们将从环境准备、示例代码运行和详解三个方面,带你快速入门 eBPF
开发。
📌 读完这篇文章,你将学会:
✔️ 如何用 Go
+ libbpfgo
开发 eBPF
程序。
✔️ 如何编写、编译、运行 eBPF
代码。
✔️ 掌握 eBPF
事件处理的工作原理。
✔️ 完整可运行的 eBPF
示例代码(持续更新)。
1.1 eBPF 简介
eBPF
(Extended Berkeley Packet Filter)是一项革命性
的Linux内核
技术,它允许在不修改内核代码的情况下,把用户自定义的代码插入到内核中运行;不仅如此,使用eBPF+uprobe
你甚至可以把自定义的代码插入到其它任意应用程序
中。想象一下吧,这意味着你几乎可以做任何事情,尤其是在安全
领域!
现在 eBPF
被广泛应用于以下领域,包括但不限于:
- 网络监控:如
Cilium
等项目利用eBPF
实现高效的网络数据包处理。 - 安全审计:可以用来拦截系统调用,实现进程级别的安全策略。
- 性能分析:
eBPF
允许深入内核内部,分析I/O
、调度、内存等性能指标。
1.2 为何选择 Go + libbpfgo
下表对比了常见的 eBPF
开发框架
开发框架 | 语言 | 依赖 | 部署复杂度 | 性能 | 优点 | 缺点 |
---|---|---|---|---|---|---|
BCC | Python + C | 需要 Python 运行环境 | 复杂 | 中等 | 生态成熟,大量示例和工具支持 | 依赖 Python,部署复杂,性能较低 |
libbpf | C | 无额外依赖 | 简单 | 高 | 官方推荐,性能最佳,适合生产环境 | 开发难度高,需要熟悉 C 语言 |
libbpfgo | Go + C | 依赖 libbpf | 简单 | 高 | Go 语言封装,Go 生态友好, 开发效率高 | 依赖cgo 调用 libbpf |
cilium/ebpf | Go + C | 纯 Go 实现,无需 libbpf | 简单 | 高 | 无 libbpf 依赖,Go 生态友好 | 功能覆盖较 libbpf 少 |
rust-bpf | Rust | 需要 Rust toolchain | 复杂 | 高 | 安全性强,Rust 生态 | Rust eBPF 生态尚不成熟,工具链复杂 |
📌 推荐选择:
- 快速验证原型 ➝
BCC
- 最高性能 & 生产环境 ➝
libbpf
- Go 生态 & 轻量部署 ➝
cilium/ebpf
- Go 生态 & libbpf 兼容 ➝
libbpfgo
- Rust 生态 & 安全性优先 ➝
rust-bpf
libbpfgo
是libbpf
的Go语言绑定, 拥有官方libbpf
的功能,同时兼顾了Go语言
的易用性和生态,非常适合快速开发和生产环境使用。
如果你没有
Go语言
基础也没关系,Go语言
的代码非常简单易懂, 有C语言
基础的同学也可以快速上手。
如果你也没有
C语言
基础, 那么本专栏可能不适合你
让我们开始吧
2. 环境准备
为了确保你的 eBPF
代码能够顺利运行,我们使用 Ubuntu 24.04 作为开发环境。
2.1 依赖工具安装
运行以下命令安装所需工具:
sudo apt update
sudo apt install -y clang llvm make git
2.2 安装 Go
确保你安装了 Go 1.22 及以上版本:
sudo apt install -y golang
go version # 确保 Go 已正确安装
3. 运行示例代码
3.1 下载示例代码
我们使用 cj-ebpf
这个示例项目, 这是专门为本专栏开发的ebpf实战项目:
git clone https://gitcode.com/weixin_47763623/cj-ebpf.git
代码目录结构如下:
cj-ebpf/libbpfgo-eg
├── 000-hello # hello world 示例代码
├── libbpf # libbpf库源码
├── common # ebpf常用函数封装
└── util # go常用函数封装
3.2 编译和运行 hello 示例
进入 000-hello
目录:
cd cj-ebpf/libbpfgo-eg
go mod tidy # 处理 Go 依赖
cd 000-hello
./build.sh # 编译 eBPF 代码
执行 eBPF
程序:
sudo ./bin/hello
如果运行成功,你应该会看到 eBPF
输出的日志。
下面我们就从这个hello示例代码开始,详细介绍 eBPF
程序的编写。
4. hello 示例代码详解
这个示例的功能是, 通过跟踪 execve
系统调用, 监控所有进程的执行, 并把日志输出到终端。
4.1 项目结构
示例项目的结构如下:
000-hello
├── bin # 编译输出目录
│ ├── bpf.o # eBPF 目标文件
│ └── hello # 可执行文件
├── build.sh # 编译脚本
└── src # 源码├── c # C 语言 BPF 代码│ ├── hello.bpf.c # BPF 代码│ └── Makefile # bpf 编译规则├── event.go # Go 事件处理├── main.go # Go 入口文件└── Makefile # 顶层 Makefile, 编译Go程序和BPF程序
🔹 1. bin/
- 编译输出目录
bpf.o
:hello.bpf.c
编译后的eBPF
目标文件,由clang
编译生成。hello
:最终的Go
可执行文件,由Go
编译生成。
🔹 2. build.sh
- 一键编译脚本
- 负责调用
Makefile
编译eBPF
和Go
代码。
🔹 3. src/
- 源代码
-
c/
目录:存放eBPF
代码(C 语言)。hello.bpf.c
:核心eBPF
逻辑代码,定义kprobe
/tracepoint
等BPF
处理逻辑。Makefile
:使用clang
和LLVM
工具链编译BPF
代码。
-
event.go
:处理BPF
事件,如perf buffer
读取、ring buffer
事件回调等。 -
main.go
:- 加载
eBPF
程序 (hello.bpf.o
)。 - 注册事件监听 (
perf buffer
或ring buffer
)。 - 运行事件循环,等待
BPF
事件触发并打印日志。
- 加载
-
Makefile
:- 顶层 Makefile,先调用
src/c/Makefile
编译BPF
程序 - 然后再编译
Go
。
- 顶层 Makefile,先调用
4.1.1 顶层 Makefile
这部分 Makefile
主要负责管理编译过程,包括编译 C 语言程序、BPF 程序、以及 Go 程序的构建工作。整体的工作流程如下:
1. 基本配置与变量
路径和工具链的配置:
SRC_ROOT = $(dir $(CURDIR)/)
OUTPUT = $(SRC_ROOT)/../../output
BIN = $(SRC_ROOT)/../bin
SRC_ROOT
是源代码的根目录。OUTPUT
和BIN
分别是输出文件和可执行文件的路径。
然后是应用的名称和 libbpf.a
的位置:
APP_NAME = hello
LIBBPF_OBJ = $(abspath $(OUTPUT)/libbpf.a)
APP_NAME
指定最终生成的应用程序的名称。LIBBPF_OBJ
指定了libbpf.a
静态库的位置。
2. 编译选项
编译和链接选项的设置:
CFLAGS = -ggdb -gdwarf -O2 -Wall -fpie -Wno-unused-variable -Wno-unused-functionCGO_CFLAGS_STATIC = "-I$(abspath $(OUTPUT))"
CGO_LDFLAGS_STATIC = "-lelf -lz -lzstd $(LIBBPF_OBJ)"
CGO_EXTLDFLAGS_STATIC = '-w -extldflags "-static"'
CFLAGS
设置了编译器的常见选项,包括调试信息和优化。- 对于 Go 程序,
CGO_CFLAGS_STATIC
和CGO_LDFLAGS_STATIC
配置了静态编译的选项。
3. 目标与规则
.PHONY: all clean
.PHONY
用于声明make
的伪目标,确保即使存在同名的文件也不会影响目标的执行。
接下来是 all
目标,即默认构建目标:
all: $(APP_NAME)
- 默认目标是构建
hello
应用程序。
构建 hello
应用程序的规则如下:
$(APP_NAME):$(MAKE) -C ./c buildCC=$(CLANG) \CGO_CFLAGS=$(CGO_CFLAGS_STATIC) \CGO_LDFLAGS=$(CGO_LDFLAGS_STATIC) \GOARCH=$(GOARCH) \go build \-tags netgo -ldflags $(CGO_EXTLDFLAGS_STATIC) \-o $(BIN)/$(APP_NAME) ./*.go@echo "build $(APP_NAME) success"
- 该规则首先通过
$(MAKE) -C ./c build
编译 BPF 程序。 - 然后,使用
clang
编译器和静态链接选项构建 Go 应用,最终生成可执行文件hello
。
4.1.2 ebpf的 Makefile
该 Makefile
主要负责编译与构建 eBPF 程序,确保所需的库、工具和头文件已经正确配置。
1. 路径定义
SRC_ROOT = $(dir $(CURDIR)/../)
PROJ_ROOT = $(SRC_ROOT)/../../
OUTPUT = $(SRC_ROOT)/../../output
BIN = $(SRC_ROOT)/../bin
LIBBPF = $(abspath $(SRC_ROOT)/../../libbpf)
这些变量定义了项目的根目录、输出目录和 libbpf
库的路径。 LIBBPF
路径是指向 libbpf
的源代码所在位置。
2. bpftool
和 vmlinux.h
配置
BPFTOOL = $(shell which bpftool || /bin/false)
BTFFILE = /sys/kernel/btf/vmlinux
DBGVMLINUX = /usr/lib/debug/boot/vmlinux-$(shell uname -r)
这部分检查系统中是否安装了 bpftool
工具,并指定内核的 vmlinux.h
文件和调试版本的内核 vmlinux
文件路径。bpftool
是用来提取内核的 BTF (BPF Type Format) 信息的工具。
3. 编译选项
CFLAGS = -ggdb -gdwarf -O2 -Wall -fpie -Wno-unused-variable -Wno-unused-function -I$(abspath $(PROJ_ROOT))
LDFLAGS =
这些是编译器的选项,包括调试信息(-ggdb -gdwarf
)、优化选项(-O2
)、警告设置(-Wall
)等。通过 -I
标志添加项目根目录到包含路径。
4. 目标和规则
-
编译
bpf.o
bpf.o: hello.bpf.c $(OUTPUT)/vmlinux.h$(CLANG) $(CFLAGS) -target bpf -D__TARGET_ARCH_x86 -I. -I$(OUTPUT) -c $< -o $(BIN)/$@
该规则编译 eBPF 程序
hello.bpf.c
,使用clang
编译器和指定的编译选项。-target bpf
表示这是一个 eBPF 程序,-D__TARGET_ARCH_x86
指定了目标架构。 -
编译
vmlinux.h
$(OUTPUT)/vmlinux.h: ifeq ($(wildcard $(BPFTOOL)),)@echo "ERROR: could not find bpftool"@exit 1 endif
首先检查系统是否有
bpftool
,如果没有则报错。接着检查内核是否支持 BTF 格式,并通过bpftool
提取或生成vmlinux.h
文件。 -
编译
libbpf
libbpf: $(LIBBPF_STATIC)$(LIBBPF_STATIC): $(LIBBPF_SRC) $(wildcard $(LIBBPF_SRC)/*.[ch]) | $(OUTPUT)/libbpfCC="$(CC)" CFLAGS="$(CFLAGS)" LD_FLAGS="$(LDFLAGS)" \$(MAKE) -C $(LIBBPF_SRC) \BUILD_STATIC_ONLY=1 \OBJDIR=$(LIBBPF_OBJDIR) \DESTDIR=$(LIBBPF_DESTDIR) \INCLUDEDIR= LIBDIR= UAPIDIR= prefix= libdir= install
libbpf
库被单独编译为静态库(libbpf.a
)。如果libbpf
目录中的源文件有更新,Makefile
会重新编译它。构建过程通过make
命令在libbpf
源码目录中进行。
4.2 eBPF 代码详解
本节我们详细解析hello.bpf.c
这个 eBPF
代码文件,它的作用是监听 execve
系统调用,并将相关信息(包括进程 pid
、ppid
、执行的文件名等)发送到用户空间。
4.2.1 代码整体逻辑
这段 eBPF
代码的主要功能是捕获 execve
系统调用,并将进程相关信息上报到用户空间,它的核心逻辑如下:
- 定义
perf
事件:使用BPF_MAP_TYPE_PERF_EVENT_ARRAY
来存储事件,并供eBPF
代码向用户空间发送数据。 - 定义事件结构体:
event_t
结构体存储execve
事件的相关信息,包括进程pid
、父进程ppid
、进程名称comm
、执行的文件名filename
等。 - 跟踪
execve
系统调用:使用tracepoint
机制监听sys_enter_execve
,当execve
发生时,获取相关进程信息并存入event_t
结构体。 - 读取参数:通过
bpf_probe_read_user_str
读取execve
调用的第一个参数,即filename
,确保能够正确读取用户空间字符串。 - 发送事件到用户空间:使用
bpf_perf_event_output
将事件数据发送到perf
事件数组,供用户空间的BPF
程序读取和处理。
4.2.2 代码细节解析
接下来,我们对代码进行详细拆解。
1️⃣ 头文件 & 许可证
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <bpf/bpf_core_read.h>char _license[] SEC("license") = "GPL";
- 代码包含
bpf_helpers.h
、bpf_tracing.h
和bpf_core_read.h
,分别提供BPF
辅助函数、tracepoint
相关定义和BPF
内核数据访问函数。 _license
变量用于指定eBPF
程序的许可证,这里声明为GPL
,确保内核可以加载该eBPF
代码。
2️⃣ 定义 perf
事件
struct {__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);__uint(key_size, sizeof(u32));__uint(value_size, sizeof(u32));
} events SEC(".maps");
- 这里定义了
BPF_MAP_TYPE_PERF_EVENT_ARRAY
,用于存储perf
事件,允许BPF
程序向用户空间发送数据。 - 该
map
在bpf_perf_event_output
函数中被使用。
3️⃣ 事件结构体
#define FILE_NAME_MAX 256struct event_t {pid_t ppid;pid_t pid;int ret;char comm[16];char filename[FILE_NAME_MAX];
};
event_t
结构体用于存储execve
事件的信息,包括:ppid
:执行execve
的进程的父进程 ID。pid
:执行execve
的进程 ID。comm
:进程名称,最大长度16
(与task_struct
结构体中的comm
字段一致)。filename
:execve
执行的文件路径,最大长度256
。
4️⃣ execve
事件跟踪
SEC("tp/syscalls/sys_enter_execve")
int trace_execve(struct trace_event_raw_sys_enter *ctx) {
SEC("tp/syscalls/sys_enter_execve")
定义了eBPF
代码的tracepoint
,绑定到sys_enter_execve
事件,即在进程执行execve
之前触发。- 该
tracepoint
的参数ctx
是sys_enter_execve
事件的trace_event_raw_sys_enter
结构体,它包含系统调用的参数信息。
5️⃣ 读取进程信息
struct event_t event = { 0, };
struct task_struct *task;task = (struct task_struct*)bpf_get_current_task();
- 这里定义
event
结构体,用于存储事件数据,并初始化为0
。 - 使用
bpf_get_current_task()
获取当前task_struct
,这是内核中表示进程的结构体。
6️⃣ 读取 pid
& ppid
event.ppid = (pid_t)BPF_CORE_READ(task, real_parent, tgid);
event.pid = bpf_get_current_pid_tgid() >> 32;
BPF_CORE_READ(task, real_parent, tgid)
通过BPF
辅助函数读取当前进程的父进程 ID(ppid
)。bpf_get_current_pid_tgid()
获取当前进程的pid
和tgid
,其中高 32 位是pid
,低 32 位是tgid
,所以右移32
位得到pid
。
7️⃣ 读取进程名称
bpf_get_current_comm(&event.comm, sizeof(event.comm));
bpf_get_current_comm()
读取当前进程的名称,并存入event.comm
,名称最大长度为16
。
8️⃣ 读取 execve
文件名
bpf_probe_read_user_str(&event.filename, sizeof(event.filename), (char *)ctx->args[0]);
ctx->args[0]
是execve
调用的第一个参数,即filename
(需要读取的可执行文件路径)。bpf_probe_read_user_str()
用于安全地从用户空间读取字符串,并存入event.filename
。
⚠️ 早期代码可能使用
BPF_CORE_READ(ctx, args[0])
,但bpf_probe_read_user_str()
更安全,能确保字符串正确终止,避免越界读取。
9️⃣ 发送事件到用户空间
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &event, sizeof(event));
bpf_perf_event_output()
将event
结构体中的数据发送到perf
事件数组events
,供用户空间程序接收。BPF_F_CURRENT_CPU
表示数据写入当前 CPU 绑定的perf
事件队列。
🔟 返回值
return 0;
eBPF
程序必须返回int
,这里返回0
,表示处理完execve
事件,不干涉系统行为。
4.2.3 小结
✅ 这段 eBPF
代码的核心功能是监听 execve
调用,并将进程相关信息上报。
✅ 代码逻辑:捕获事件 → 读取进程信息 → 读取 execve
参数 → 发送事件到用户空间。
✅ 关键技术点:
- 使用
tracepoint
监听sys_enter_execve
📌 - 使用
BPF_CORE_READ
读取进程ppid
🏷️ - 使用
bpf_probe_read_user_str()
读取用户空间数据 📄 - 使用
bpf_perf_event_output()
发送事件 📤
这样,我们就能在用户空间监听 execve
事件,并收集相关进程信息 🎯。
4.3 入口 main.go
下面我们就来详细看一下用户空间的 Go
代码。
在这一部分,我们将逐行分析main.go
的代码,理解程序的整体逻辑,并深入探讨其中的关键细节。
4.3.1 代码整体逻辑
main.go
的目的是加载并运行一个eBPF程序,通过perf buffer
接收和处理事件,同时监听退出信号来优雅地关闭程序。具体来说,程序执行的流程如下:
- 信号处理:程序会注册信号处理函数,监听
SIGINT
(中断信号)和SIGTERM
(终止信号)。当接收到这些信号时,程序将优雅地退出。 - 加载eBPF程序:通过
util.BpfLoadAndAttach
加载指定的eBPF目标文件(bpf.o
),并附加到内核中。 - 初始化perf buffer:创建并初始化
perf buffer
,用于接收eBPF程序生成的事件。 - 处理事件:程序进入事件处理循环,持续监听
perf buffer
中的事件并打印相应的日志,直到收到退出信号。 - 退出:在收到退出信号后,程序会打印退出日志并结束。
4.3.2 代码细节解析
1. 信号处理
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
defer stop()
- 这段代码使用了
signal.NotifyContext
来创建一个可以监听系统信号(SIGINT
和SIGTERM
)的上下文ctx
。当接收到这些信号时,ctx
会被取消。 defer stop()
确保在main
函数退出时,取消信号监听,避免资源泄漏。
2. 日志设置
log.SetLevel(log.DebugLevel)
log.Debug("load bpf program")
- 这两行代码使用
logrus
包设置日志级别为Debug
,并记录一条调试日志,表明正在加载BPF程序。 log.Debug
是一种低级别的日志记录,通常用于开发过程中帮助调试。
3. 加载BPF程序
bpfModule, err := util.BpfLoadAndAttach("bpf.o")
if err != nil {log.Fatalf("%+v", err)
}
defer bpfModule.Close()
util.BpfLoadAndAttach("bpf.o")
会加载指定的eBPF程序(此处为bpf.o
),并将其附加到系统中。如果加载失败,程序会记录错误并退出。defer bpfModule.Close()
确保在程序退出时关闭eBPF模块并释放资源。
4. 创建perf buffer
eventsChannel := make(chan []byte)
lostChannel := make(chan uint64)
pb, err := bpfModule.InitPerfBuf("events", eventsChannel, lostChannel, 1024)
if err != nil {return
}
- 这里创建了两个通道:
eventsChannel
用于接收事件数据,lostChannel
用于接收丢失的事件数量。 bpfModule.InitPerfBuf
初始化了一个perf buffer
,该缓冲区会接收从eBPF程序生成的事件。1024
是缓冲区的大小,表示最多可以缓存1024个事件。
5. 启动perf buffer
pb.Start()
defer pb.Close()
pb.Start()
启动perf buffer
,开始接收事件。defer pb.Close()
确保在程序结束时关闭perf buffer
。
6. 事件处理
processEvents(eventsChannel, lostChannel, ctx)
- 程序进入
processEvents
函数,开始循环接收并处理来自eventsChannel
和lostChannel
的事件,直到收到退出信号。
7. 事件处理函数 processEvents
func processEvents(eventsChannel <-chan []byte, lostChannel <-chan uint64, ctx context.Context) {exit := falsefor {if exit {break}select {case data := <-eventsChannel:var event Event// 解析事件数据并打印日志, 下文会详细介绍Event结构err := event.Parse(data)if err != nil {log.Printf("parse event error: %v", err)} else {log.Printf("ppid: %d pid: %d comm=[%s] filename=[%s] ret=%d", event.Ppid, event.Pid,event.CommString(), event.FilenameString(), event.Ret)}case n := <-lostChannel:log.Printf("lost %d events", n)case <-ctx.Done():exit = truebreak}}
}
- 这个函数会持续监听
eventsChannel
和lostChannel
中的数据。- 当接收到事件数据时,会解析并打印事件的详细信息,如父进程ID(
ppid
)、进程ID(pid
)、进程名称(comm
)、文件名(filename
)和返回值(ret
)。 - 如果有丢失的事件,则会打印丢失事件的数量。
- 当接收到退出信号时(
ctx.Done()
),会设置exit
为true
,从而退出循环。
- 当接收到事件数据时,会解析并打印事件的详细信息,如父进程ID(
4.3.3 小结
这一部分代码展示了如何使用Go语言和eBPF结合处理系统事件。它通过注册信号处理,加载并附加eBPF程序,使用perf buffer
接收事件,并通过一个事件处理函数打印事件信息。在程序退出时,它会优雅地关闭资源。这个过程展示了eBPF在实际应用中的基础用法,以及如何高效地进行事件处理。
4.4 事件处理 event.go
在这一小节中,我们将详细解析处理 event
数据的代码。该代码的主要功能是定义了一个 Event
结构体,并提供了一些方法来解析和转换与该结构体相关的数据。
4.4.1 整体逻辑
首先,Event
结构体用于存储与某些事件相关的数据,主要包含进程ID、父进程ID、返回值、进程名称(Comm
)和文件名(Filename
)。该结构体包含的方法用于将字节数组(data
)转换成结构体字段,并处理字符串字段的显示。整个过程分为以下几步:
- 定义数据结构:
Event
结构体包含多个字段,其中有整型字段Ppid
、Pid
和Ret
,以及用于存储字符串的Comm
和Filename
字段。 - 转换字符串:通过自定义方法
CommString
和FilenameString
,将字节数组转换为更易读的字符串。 - 解析字节数据:通过
Parse
方法将传入的字节数据解析为Event
结构体实例。
4.4.2 代码细节解析
接下来,我们深入分析代码的每一部分。
- 结构体定义:
type Event struct {Ppid uint32Pid uint32Ret int32Comm [16]byteFilename [256]byte
}
这段代码定义了一个 Event
结构体,包含五个字段:
-
Ppid
和Pid
:分别表示父进程ID和进程ID,使用uint32
类型存储。 -
Ret
:表示事件返回的值,使用int32
类型,通常用于表示调用的返回状态或结果。 -
Comm
:存储进程名的字节数组,长度为 16 字节。由于进程名可能较短,因此使用固定大小的数组。 -
Filename
:存储文件名的字节数组,长度为 256 字节,用于存储与该事件相关的文件路径。 -
Comm 字段转换:
func (e *Event) CommString() string {return string(bytes.TrimRight(e.Comm[:], "\x00"))
}
该方法将 Comm
字节数组转换为字符串,并去除末尾的 \x00
(空字节)。bytes.TrimRight
函数用于去除 Comm
字段中多余的填充字节。转换后的结果是一个不包含空字节的有效进程名。
- Filename 字段转换:
func (e *Event) FilenameString() string {return string(bytes.TrimRight(e.Filename[:], "\x00"))
}
FilenameString
方法与 CommString
类似,将 Filename
字节数组转换为字符串,并去除末尾的空字节。这样便于获取该事件相关的文件路径。
- 数据解析:
func (e *Event) Parse(data []byte) error {err := binary.Read(bytes.NewBuffer(data), binary.LittleEndian, e)if err != nil {return err}return nil
}
Parse
方法用于解析字节数据并填充 Event
结构体。这里使用 binary.Read
函数将 data
字节数据按照 LittleEndian
字节序解析到 Event
结构体中。LittleEndian
表示数据的低位字节存储在低地址中,这是大多数 x86 系统采用的字节序。如果解析过程中发生错误,将返回相应的错误信息。
4.4.3 小结
在这一段代码中,我们定义了一个 Event
结构体,并为其提供了转换和解析字节数据的方法。通过 Parse
方法,我们可以将原始的字节流解析为结构体实例,方便后续的处理。CommString
和 FilenameString
方法则帮助我们将字节数组转化为易于阅读的字符串,去除多余的空字节。这个过程对于事件数据的处理至关重要,确保了我们能够高效地提取出事件相关的关键信息。
4.5 小结
通过这个hello
示例代码,我们展示了如何使用eBPF捕获内核事件(如execve
系统调用),并通过perf buffer
将事件数据传递给用户空间进行处理。在用户空间中,Go
程序则负责加载和运行eBPF程序,并实现事件的捕获与处理逻辑。
关键步骤包括:
- eBPF程序:捕获
execve
事件,读取相关进程信息(如pid
、ppid
、进程名称和文件路径),并通过perf buffer
发送到用户空间。 - 用户空间:通过
Go
程序加载eBPF程序,初始化perf buffer
,并在事件循环中处理来自内核的事件数据。
这种模式将内核事件捕获与用户空间处理结合起来,使得开发者可以高效地监控和响应内核级事件,非常适用于安全审计、性能监控等场景。
5. 总结与延伸阅读
5.1 关键知识点回顾
✅ eBPF
允许在内核中运行用户定义的代码。
✅ libbpfgo
提供了一种高效的 Go
语言 eBPF
交互方式。
✅ eBPF
代码通常分为 BPF 程序 和 用户态 Go 代码。
5.2 进一步学习
📚 推荐阅读:
- 《BPF Performance Tools》(eBPF 进阶教程)
- 官方 libbpf 文档
👏 现在,你已经完成了 Go + libbpfgo
的 eBPF
开发入门!请持续关注我的eBPF实战专栏,我们将不断更新更多有关 eBPF
的内容。
相关文章:

Go 语言 + libbpfgo 实战 eBPF 开发
Go 语言 libbpfgo 实战 eBPF 开发 1. 引言 这是专栏的第一篇文章,我们将从环境准备、示例代码运行和详解三个方面,带你快速入门 eBPF 开发。 📌 读完这篇文章,你将学会: ✔️ 如何用 Go libbpfgo 开发 eBPF 程序。…...

练习题:74
目录 Python题目 题目 题目分析 需求理解 关键知识点 实现思路分析 复杂度分析 可能遇到的问题及注意事项 代码实现 代码解释 运行思路 1. 列表定义阶段 2. for 循环启动阶段 3. 偶数判断与 continue 语句执行阶段 4. 奇数元素输出阶段 5. 循环结束阶段 结束语…...

Python 性能优化:从入门到精通的实用指南
Langchain系列文章目录 01-玩转LangChain:从模型调用到Prompt模板与输出解析的完整指南 02-玩转 LangChain Memory 模块:四种记忆类型详解及应用场景全覆盖 03-全面掌握 LangChain:从核心链条构建到动态任务分配的实战指南 04-玩转 LangChai…...

C# OPC DA获取DCS数据(提前配置DCOM)
OPC DA配置操作手册 配置完成后,访问远程ip,就能获取到服务 C#使用Interop.OPCAutomation采集OPC DA数据,支持订阅(数据变化)、单个读取、单个写入、断线重连...

xinference docker 部署方式
文章目录 简绍docker 安装方式访问地址对应官网在 dify 中 添加 xinference 容器内置大语言模型嵌入模型图像模型音频模型重排序模型视频模型 简绍 Xorbits Inference (Xinference) 是一个开源平台,用于简化各种 AI 模型的运行和集成。借助 Xinference,…...

基于Kubernetes部署MySQL主从集群
以下是一个基于Kubernetes部署MySQL主从集群的详细YAML示例,包含StatefulSet、Service、ConfigMap和Secret等关键配置。MySQL主从集群需要至少1个主节点和多个从节点,这里使用 StatefulSet 初始化脚本 实现主从自动配置。 1. 创建 Namespace (可选) ap…...

【Azure 架构师学习笔记】- Azure Databricks (17) --Delta Live Table和Delta Table
本文属于【Azure 架构师学习笔记】系列。 本文属于【Azure Databricks】系列。 接上文 【Azure 架构师学习笔记】- Azure Databricks (16) – Delta Lake 和 ADLS整合 前言 前面介绍了Delta Table,但是Databricks又推出了“Delta Live Tables(DLTs&…...

Mybatis Generator 使用手册
第一章 什么是Mybatis Generator? MyBatis Generator Core – Introduction to MyBatis Generator MyBatis生成器(MBG)是MyBatis框架的代码生成工具。它支持为所有版本的MyBatis生成代码,通过解析数据库表(或多个表&…...

快乐数 力扣202
一、题目 编写一个算法来判断一个数 n 是不是快乐数。 「快乐数」 定义为: 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。如果这个过程 结果为 1&…...

SPA单页面应用优化SEO
1.SSR服务端渲染 将组件或页面通过服务器生成html,再返回给浏览器,如nuxt.js或vue-server-renderer const Vue require(vue); const server require(express)(); const renderer require(vue-server-renderer).createRenderer();const vueApp new …...

城市霓虹灯夜景拍照后期Lr调色教程,手机滤镜PS+Lightroom预设下载!
调色教程 在城市霓虹灯夜景拍摄中,由于现场光线复杂等因素,照片可能无法完全呈现出当时的视觉感受。通过 Lr 调色,可以弥补拍摄时的不足。例如,运用基本调整面板中的曝光、对比度、阴影等工具,可以处理出画面的整体明暗…...

通领科技冲刺北交所
高质量增长奔赴产业新征程 日前,通领科技已正式启动在北交所的 IPO 进程,期望借助资本市场的力量,加速技术升级,推动全球化战略布局。这一举措不仅展现了中国汽车零部件企业的强大实力,也预示着行业转型升级的新突破。…...

隐私保护在 Facebook 用户身份验证中的应用
在这个数字化的时代,个人隐私保护成为了公众关注的焦点。社交媒体巨头 Facebook 作为全球最大的社交平台之一,拥有数十亿用户,其在用户身份验证过程中对隐私保护的重视程度直接影响着用户的安全感和信任度。本文将探讨 Facebook 在用户身份验…...

深度学习/强化学习调参技巧
深度调优策略 1. 学习率调整 技巧:学习率是最重要的超参数之一。过大可能导致训练不稳定,过小则收敛速度慢。可以使用学习率衰减(Learning Rate Decay)或自适应学习率方法(如Adam、RMSprop)来动态调整学习…...

python面试常见题目
1、python 有几种数据类型 数字:整形 (int),浮点型 (float)布尔 ( bool):false true字符串 (string)列表 (list)元组 (tuple)字典 &…...

echarts折线图设置背景颜色:X轴和Y轴组成部分背景色
echarts折线图设置背景颜色 关键代码 splitArea: {show: true,areaStyle: {color: [#F2F2F2],},},完整代码位置显示 yAxis: {type: value,boundaryGap: [0, 100%],max: 1,interval: 1,// 于设置y轴的字体axisLabel: {show: false, //这里的show用于设置是否显示y轴下的字体 默…...

文本处理Bert面试内容整理-BERT的应用场景有哪些?
BERT(Bidirectional Encoder Representations from Transformers)在多个自然语言处理(NLP)任务中表现出了强大的能力。由于其能够捕捉双向上下文信息和强大的迁移学习能力,BERT广泛应用于各种NLP场景。以下是BERT的一些典型应用场景: 1. 文本分类 文本分类任务旨在将文本…...

【愚公系列】《Python网络爬虫从入门到精通》045-Charles的SSL证书的安装
标题详情作者简介愚公搬代码头衔华为云特约编辑,华为云云享专家,华为开发者专家,华为产品云测专家,CSDN博客专家,CSDN商业化专家,阿里云专家博主,阿里云签约作者,腾讯云优秀博主&…...

manus对比ChatGPT-Deep reaserch进行研究类学术相关数据分析!谁更胜一筹?
没有账号,只能挑选一个案例 一夜之间被这个用全英文介绍全华班出品的新爆款国产AI产品的小胖刷频。白天还没有切换语言的选项,晚上就加上了。简单看了看团队够成,使用很长实践的Monica创始人也在其中。逐渐可以理解,重心放在海外产…...

20250307确认荣品PRO-RK3566开发板在Android13下的以太网络共享功能
20250307确认荣品PRO-RK3566开发板在Android13下的以太网络共享功能 2025/3/7 13:56 缘起:我司地面站需要实现“太网络共享功能”功能。电脑PC要像连接WIFI热点一样连接在Android设备/平板电脑上来实现上网功能/数据传输。 Android设备/平板电脑通过4G/WIFI来上网。…...

Unity Job系统详解原理和基础应用处理大量物体位置
概述 该脚本使用 Unity Job System 和 Burst Compiler 高效管理大量剑对象的位移计算与坐标更新。通过双缓冲技术实现无锁并行计算,适用于需要高性能批量处理Transform的场景。 核心类 SwordManager 成员变量 变量名类型说明swordPrefabGameObject剑对象预制体_d…...

高效编程指南:PyCharm与DeepSeek的完美结合
DeepSeek接入Pycharm 前几天DeepSeek的充值窗口又悄悄的开放了,这也就意味着我们又可以丝滑的使用DeepSeek的API进行各种辅助性工作了。本文我们来聊聊如何在代码编辑器中使用DeepSeek自动生成代码。 注:本文适用于所有的JetBrains开发工具,…...

Facebook 的隐私保护数据存储方案研究
Facebook 的隐私保护数据存储方案研究 在这个信息爆炸的时代,数据隐私保护已成为公众关注的热点。Facebook,作为全球最大的社交媒体平台之一,承载着海量用户数据,其隐私保护措施和数据存储方案对于维护用户隐私至关重要。本文将深…...

c#面试题整理
1.如何保持数据库的完整性,一致性 最好的方法:数据库约束(check,unique,主键,外键,默认,非空) 其次是:用触发器 最后:才是自己些业务逻辑,这个效率低 2.事…...

车载以太网测试-4车载以太网如何进行通信的?
1 摘要 车载以太网的数据传输与接收遵循分层网络架构(如OSI模型或TCP/IP模型),从应用层到物理层需要逐层封装与解封装。本文将对车载以太网的数据传输流程进行介绍。 2 以太网通信过程(封装与解封装) 2.1 发送端流程…...

R软件线性模型与lmer混合效应模型对生态学龙类智力测试数据层级结构应用
全文链接:https://tecdat.cn/?p40925 在生态与生物学研究中,数据常呈现复杂结构特征。例如不同种群、采样点或时间序列的观测数据间往往存在相关性(点击文末“阅读原文”获取完整代码、数据、文档)。 传统线性模型在处理这类非独…...

WIFI ESP8266以及基础功能介绍
芯片一旦烧写了程序就不可以使用AT指令集,需要重新刷回AT指令库才可以使用 wifi的通信频段是2.4G免费频段。 AT指令 AT(attention)command set.AT指令集或命令集,一般称为AT指令 海斯命令集:Hayes command set 默认…...

HarmonyOS ArkTS声明式UI开发实战教程
引言:为何选择ArkTS? 在HarmonyOS生态快速发展的当下,ArkTS作为新一代声明式UI开发框架,正在引发移动应用开发范式的变革。笔者曾在多个跨平台框架开发中经历过"命令式编程之痛",直到接触ArkTS后才发现&…...

FPGA之USB通信实战:基于FX2芯片的Slave FIFO回环测试详解
FPGA之Usb数据传输 Usb 通信 你也许会有疑问,明明有这么多通信方式和数据传输(SPI、I2C、UART、以太网)为什么偏偏使用USB呢? 原因有很多,如下: 1. 高速数据传输能力 高带宽:USB接口提供了较高的数据传…...

【MySQL_03】数据库基本--核心概念
文章目录 一、数据库基础1.1 数据库基础定义1.2 数据库分类与典型产品1.3 数据库模型1.4 数据库层次结构1.5 数据库核心机制1.6 数据表和视图1.61 数据表(Table)1.62 视图(View) 1.7 键类型1.8 MySQL数据类型1.9 数据库范式化 二、…...