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.obpf.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文件。 -
编译
libbpflibbpf: $(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= installlibbpf库被单独编译为静态库(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来上网。…...
定时器任务——若依源码分析
分析util包下面的工具类schedule utils: ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类,封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz,先构建任务的 JobD…...
c++ 面试题(1)-----深度优先搜索(DFS)实现
操作系统:ubuntu22.04 IDE:Visual Studio Code 编程语言:C11 题目描述 地上有一个 m 行 n 列的方格,从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子,但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...
Module Federation 和 Native Federation 的比较
前言 Module Federation 是 Webpack 5 引入的微前端架构方案,允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...
【python异步多线程】异步多线程爬虫代码示例
claude生成的python多线程、异步代码示例,模拟20个网页的爬取,每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程:允许程序同时执行多个任务,提高IO密集型任务(如网络请求)的效率…...
数据库分批入库
今天在工作中,遇到一个问题,就是分批查询的时候,由于批次过大导致出现了一些问题,一下是问题描述和解决方案: 示例: // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...
Java多线程实现之Thread类深度解析
Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...
Mysql中select查询语句的执行过程
目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析(Parser) 2.4、执行sql 1. 预处理(Preprocessor) 2. 查询优化器(Optimizer) 3. 执行器…...
音视频——I2S 协议详解
I2S 协议详解 I2S (Inter-IC Sound) 协议是一种串行总线协议,专门用于在数字音频设备之间传输数字音频数据。它由飞利浦(Philips)公司开发,以其简单、高效和广泛的兼容性而闻名。 1. 信号线 I2S 协议通常使用三根或四根信号线&a…...
Vite中定义@软链接
在webpack中可以直接通过符号表示src路径,但是vite中默认不可以。 如何实现: vite中提供了resolve.alias:通过别名在指向一个具体的路径 在vite.config.js中 import { join } from pathexport default defineConfig({plugins: [vue()],//…...
【从零开始学习JVM | 第四篇】类加载器和双亲委派机制(高频面试题)
前言: 双亲委派机制对于面试这块来说非常重要,在实际开发中也是经常遇见需要打破双亲委派的需求,今天我们一起来探索一下什么是双亲委派机制,在此之前我们先介绍一下类的加载器。 目录 编辑 前言: 类加载器 1. …...
