当前位置: 首页 > news >正文

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 开发框架

开发框架语言依赖部署复杂度性能优点缺点
BCCPython + C需要 Python 运行环境复杂中等生态成熟,大量示例和工具支持依赖 Python,部署复杂,性能较低
libbpfC无额外依赖简单官方推荐,性能最佳,适合生产环境开发难度高,需要熟悉 C 语言
libbpfgoGo + C依赖 libbpf简单Go 语言封装,Go 生态友好, 开发效率高依赖cgo调用 libbpf
cilium/ebpfGo + C纯 Go 实现,无需 libbpf简单无 libbpf 依赖,Go 生态友好功能覆盖较 libbpf 少
rust-bpfRust需要 Rust toolchain复杂安全性强,Rust 生态Rust eBPF 生态尚不成熟,工具链复杂

📌 推荐选择

  • 快速验证原型BCC
  • 最高性能 & 生产环境libbpf
  • Go 生态 & 轻量部署cilium/ebpf
  • Go 生态 & libbpf 兼容libbpfgo
  • Rust 生态 & 安全性优先rust-bpf

libbpfgolibbpf的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.ohello.bpf.c 编译后的 eBPF 目标文件,由 clang 编译生成。
  • hello:最终的 Go 可执行文件,由 Go 编译生成。

🔹 2. build.sh - 一键编译脚本

  • 负责调用 Makefile 编译 eBPFGo 代码。

🔹 3. src/ - 源代码

  • c/ 目录:存放 eBPF 代码(C 语言)。

    • hello.bpf.c:核心 eBPF 逻辑代码,定义 kprobe/tracepointBPF 处理逻辑。
    • Makefile:使用 clangLLVM 工具链编译 BPF 代码。
  • event.go:处理 BPF 事件,如 perf buffer 读取、ring buffer 事件回调等。

  • main.go

    • 加载 eBPF 程序 (hello.bpf.o)。
    • 注册事件监听 (perf bufferring buffer)。
    • 运行事件循环,等待 BPF 事件触发并打印日志。
  • Makefile

    • 顶层 Makefile,先调用 src/c/Makefile 编译 BPF程序
    • 然后再编译 Go
4.1.1 顶层 Makefile

这部分 Makefile 主要负责管理编译过程,包括编译 C 语言程序、BPF 程序、以及 Go 程序的构建工作。整体的工作流程如下:

1. 基本配置与变量

路径和工具链的配置:

SRC_ROOT = $(dir $(CURDIR)/)
OUTPUT = $(SRC_ROOT)/../../output
BIN = $(SRC_ROOT)/../bin
  • SRC_ROOT 是源代码的根目录。
  • OUTPUTBIN 分别是输出文件和可执行文件的路径。

然后是应用的名称和 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_STATICCGO_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. bpftoolvmlinux.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 系统调用,并将相关信息(包括进程 pidppid、执行的文件名等)发送到用户空间。


4.2.1 代码整体逻辑

这段 eBPF 代码的主要功能是捕获 execve 系统调用,并将进程相关信息上报到用户空间,它的核心逻辑如下:

  1. 定义 perf 事件:使用 BPF_MAP_TYPE_PERF_EVENT_ARRAY 来存储事件,并供 eBPF 代码向用户空间发送数据。
  2. 定义事件结构体event_t 结构体存储 execve 事件的相关信息,包括进程 pid、父进程 ppid、进程名称 comm、执行的文件名 filename 等。
  3. 跟踪 execve 系统调用:使用 tracepoint 机制监听 sys_enter_execve,当 execve 发生时,获取相关进程信息并存入 event_t 结构体。
  4. 读取参数:通过 bpf_probe_read_user_str 读取 execve 调用的第一个参数,即 filename,确保能够正确读取用户空间字符串。
  5. 发送事件到用户空间:使用 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.hbpf_tracing.hbpf_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 程序向用户空间发送数据。
  • mapbpf_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 字段一致)。
    • filenameexecve 执行的文件路径,最大长度 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 的参数 ctxsys_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() 获取当前进程的 pidtgid,其中高 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接收和处理事件,同时监听退出信号来优雅地关闭程序。具体来说,程序执行的流程如下:

  1. 信号处理:程序会注册信号处理函数,监听SIGINT(中断信号)和SIGTERM(终止信号)。当接收到这些信号时,程序将优雅地退出。
  2. 加载eBPF程序:通过util.BpfLoadAndAttach加载指定的eBPF目标文件(bpf.o),并附加到内核中。
  3. 初始化perf buffer:创建并初始化perf buffer,用于接收eBPF程序生成的事件。
  4. 处理事件:程序进入事件处理循环,持续监听perf buffer中的事件并打印相应的日志,直到收到退出信号。
  5. 退出:在收到退出信号后,程序会打印退出日志并结束。
4.3.2 代码细节解析
1. 信号处理
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
defer stop()
  • 这段代码使用了signal.NotifyContext来创建一个可以监听系统信号(SIGINTSIGTERM)的上下文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函数,开始循环接收并处理来自eventsChannellostChannel的事件,直到收到退出信号。
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}}
}
  • 这个函数会持续监听eventsChannellostChannel中的数据。
    • 当接收到事件数据时,会解析并打印事件的详细信息,如父进程ID(ppid)、进程ID(pid)、进程名称(comm)、文件名(filename)和返回值(ret)。
    • 如果有丢失的事件,则会打印丢失事件的数量。
    • 当接收到退出信号时(ctx.Done()),会设置exittrue,从而退出循环。
4.3.3 小结

这一部分代码展示了如何使用Go语言和eBPF结合处理系统事件。它通过注册信号处理,加载并附加eBPF程序,使用perf buffer接收事件,并通过一个事件处理函数打印事件信息。在程序退出时,它会优雅地关闭资源。这个过程展示了eBPF在实际应用中的基础用法,以及如何高效地进行事件处理。

4.4 事件处理 event.go

在这一小节中,我们将详细解析处理 event 数据的代码。该代码的主要功能是定义了一个 Event 结构体,并提供了一些方法来解析和转换与该结构体相关的数据。

4.4.1 整体逻辑

首先,Event 结构体用于存储与某些事件相关的数据,主要包含进程ID、父进程ID、返回值、进程名称(Comm)和文件名(Filename)。该结构体包含的方法用于将字节数组(data)转换成结构体字段,并处理字符串字段的显示。整个过程分为以下几步:

  1. 定义数据结构Event 结构体包含多个字段,其中有整型字段 PpidPidRet,以及用于存储字符串的 CommFilename 字段。
  2. 转换字符串:通过自定义方法 CommStringFilenameString,将字节数组转换为更易读的字符串。
  3. 解析字节数据:通过 Parse 方法将传入的字节数据解析为 Event 结构体实例。
4.4.2 代码细节解析

接下来,我们深入分析代码的每一部分。

  • 结构体定义
type Event struct {Ppid     uint32Pid      uint32Ret      int32Comm     [16]byteFilename [256]byte
}

这段代码定义了一个 Event 结构体,包含五个字段:

  • PpidPid:分别表示父进程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 方法,我们可以将原始的字节流解析为结构体实例,方便后续的处理。CommStringFilenameString 方法则帮助我们将字节数组转化为易于阅读的字符串,去除多余的空字节。这个过程对于事件数据的处理至关重要,确保了我们能够高效地提取出事件相关的关键信息。


4.5 小结

通过这个hello示例代码,我们展示了如何使用eBPF捕获内核事件(如execve系统调用),并通过perf buffer将事件数据传递给用户空间进行处理。在用户空间中,Go程序则负责加载和运行eBPF程序,并实现事件的捕获与处理逻辑。

关键步骤包括:

  • eBPF程序:捕获execve事件,读取相关进程信息(如pidppid、进程名称和文件路径),并通过perf buffer发送到用户空间。
  • 用户空间:通过Go程序加载eBPF程序,初始化perf buffer,并在事件循环中处理来自内核的事件数据。

这种模式将内核事件捕获与用户空间处理结合起来,使得开发者可以高效地监控和响应内核级事件,非常适用于安全审计、性能监控等场景。

5. 总结与延伸阅读

5.1 关键知识点回顾

eBPF 允许在内核中运行用户定义的代码。
libbpfgo 提供了一种高效的 Go 语言 eBPF 交互方式。
eBPF 代码通常分为 BPF 程序用户态 Go 代码

5.2 进一步学习

📚 推荐阅读:

  • 《BPF Performance Tools》(eBPF 进阶教程)
  • 官方 libbpf 文档

👏 现在,你已经完成了 Go + libbpfgoeBPF 开发入门!请持续关注我的eBPF实战专栏,我们将不断更新更多有关 eBPF 的内容。

相关文章:

Go 语言 + libbpfgo 实战 eBPF 开发

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

练习题:74

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

Python 性能优化:从入门到精通的实用指南

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

C# OPC DA获取DCS数据(提前配置DCOM)

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

xinference docker 部署方式

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

基于Kubernetes部署MySQL主从集群

以下是一个基于Kubernetes部署MySQL主从集群的详细YAML示例&#xff0c;包含StatefulSet、Service、ConfigMap和Secret等关键配置。MySQL主从集群需要至少1个主节点和多个从节点&#xff0c;这里使用 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&#xff0c;但是Databricks又推出了“Delta Live Tables&#xff08;DLTs&…...

Mybatis Generator 使用手册

第一章 什么是Mybatis Generator&#xff1f; MyBatis Generator Core – Introduction to MyBatis Generator MyBatis生成器&#xff08;MBG&#xff09;是MyBatis框架的代码生成工具。它支持为所有版本的MyBatis生成代码&#xff0c;通过解析数据库表&#xff08;或多个表&…...

快乐数 力扣202

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

SPA单页面应用优化SEO

1.SSR服务端渲染 将组件或页面通过服务器生成html&#xff0c;再返回给浏览器&#xff0c;如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预设下载!

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

通领科技冲刺北交所

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

隐私保护在 Facebook 用户身份验证中的应用

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

深度学习/强化学习调参技巧

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

python面试常见题目

1、python 有几种数据类型 数字:整形 &#xff08;int&#xff09;,浮点型 &#xff08;float&#xff09;布尔 &#xff08; bool&#xff09;:false true字符串 &#xff08;string&#xff09;列表 &#xff08;list&#xff09;元组 &#xff08;tuple&#xff09;字典 &…...

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证书的安装

标题详情作者简介愚公搬代码头衔华为云特约编辑&#xff0c;华为云云享专家&#xff0c;华为开发者专家&#xff0c;华为产品云测专家&#xff0c;CSDN博客专家&#xff0c;CSDN商业化专家&#xff0c;阿里云专家博主&#xff0c;阿里云签约作者&#xff0c;腾讯云优秀博主&…...

manus对比ChatGPT-Deep reaserch进行研究类学术相关数据分析!谁更胜一筹?

没有账号&#xff0c;只能挑选一个案例 一夜之间被这个用全英文介绍全华班出品的新爆款国产AI产品的小胖刷频。白天还没有切换语言的选项&#xff0c;晚上就加上了。简单看了看团队够成&#xff0c;使用很长实践的Monica创始人也在其中。逐渐可以理解&#xff0c;重心放在海外产…...

20250307确认荣品PRO-RK3566开发板在Android13下的以太网络共享功能

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

[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解

突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 ​安全措施依赖问题​ GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...

C++:std::is_convertible

C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...

多种风格导航菜单 HTML 实现(附源码)

下面我将为您展示 6 种不同风格的导航菜单实现&#xff0c;每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...

【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)

1.获取 authorizationCode&#xff1a; 2.利用 authorizationCode 获取 accessToken&#xff1a;文档中心 3.获取手机&#xff1a;文档中心 4.获取昵称头像&#xff1a;文档中心 首先创建 request 若要获取手机号&#xff0c;scope必填 phone&#xff0c;permissions 必填 …...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

算法:模拟

1.替换所有的问号 1576. 替换所有的问号 - 力扣&#xff08;LeetCode&#xff09; ​遍历字符串​&#xff1a;通过外层循环逐一检查每个字符。​遇到 ? 时处理​&#xff1a; 内层循环遍历小写字母&#xff08;a 到 z&#xff09;。对每个字母检查是否满足&#xff1a; ​与…...

处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的

修改bug思路&#xff1a; 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑&#xff1a;async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...

GO协程(Goroutine)问题总结

在使用Go语言来编写代码时&#xff0c;遇到的一些问题总结一下 [参考文档]&#xff1a;https://www.topgoer.com/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/goroutine.html 1. main()函数默认的Goroutine 场景再现&#xff1a; 今天在看到这个教程的时候&#xff0c;在自己的电…...

Rust 开发环境搭建

环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行&#xff1a; rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu ​ 2、Hello World fn main() { println…...

掌握 HTTP 请求:理解 cURL GET 语法

cURL 是一个强大的命令行工具&#xff0c;用于发送 HTTP 请求和与 Web 服务器交互。在 Web 开发和测试中&#xff0c;cURL 经常用于发送 GET 请求来获取服务器资源。本文将详细介绍 cURL GET 请求的语法和使用方法。 一、cURL 基本概念 cURL 是 "Client URL" 的缩写…...