如果覺得文章對你有所啟發,可以考慮用 🌟 支持 Gthulhu 專案,短期目標是集齊 300 個 🌟 藉此被 CNCF Landscape 採納 [ref]。
在前面的文章中,我們體驗了 eBPF 程式的強大功能,但您可能已經注意到一個問題:eBPF 程式在不同的核心版本或系統配置上可能無法正常工作。這是因為核心資料結構在不同版本間可能發生變化,導致程式相容性問題。
今天我們將深入學習 CO-RE (Compile Once, Run Everywhere) 技術,這是解決 eBPF 程式可移植性問題的關鍵技術。通過 CO-RE,我們可以編寫一次 eBPF 程式,然後在任何支援的核心版本上運行。
筆者補充:
在 CO-RE 出現之前,我們可以使用 BCC 動態的載入 eBPF program 以解決 portability 的問題。但是對於 container-based service 來說,BCC 依賴的 tool chain 實在太大太雜了。因此,eBPF 社群就透過 relocation 的方式使 eBPF 能夠做到 Compile Once, Run Everywhere!但是使用 CO-RE 仍須注意 helper function、kfuncs 以及不同 program type 在跨版本支援度的問題唷。
CO-RE 是 eBPF 生態系統中的一項革命性技術,它包含三個核心組件:
在 CO-RE 之前,eBPF 程式面臨以下挑戰:
// 傳統方式:硬編碼偏移量
struct task_struct *task = (struct task_struct *)bpf_get_current_task();
pid_t pid = *(pid_t *)((char *)task + 1248); // 偏移量在不同核心版本間可能改變
這種方式的問題:
// CO-RE 方式:使用 BPF_CORE_READ
struct task_struct *task = (struct task_struct *)bpf_get_current_task();
pid_t pid = BPF_CORE_READ(task, pid); // 自動處理不同核心版本的差異
CO-RE 的優勢:
BTF 是一種緊湊的元資料格式,描述了 C 資料類型:
# 查看核心 BTF 資訊
bpftool btf dump file /sys/kernel/btf/vmlinux format c
# 查看特定結構體
bpftool btf dump file /sys/kernel/btf/vmlinux | grep "struct task_struct"
# 從核心 DWARF 資訊生成 BTF
pahole --btf_encode_detached vmlinux vmlinux.btf
# 檢查 BTF 格式
bpftool btf dump file vmlinux.btf format raw
// BTF 描述的 task_struct 結構
[1] STRUCT 'task_struct' size=8704 vlen=207
'state' type_id=2 bits_offset=0
'stack' type_id=3 bits_offset=64
'usage' type_id=4 bits_offset=128
'flags' type_id=5 bits_offset=160
'ptrace' type_id=5 bits_offset=192
'on_cpu' type_id=6 bits_offset=224
'wake_entry' type_id=7 bits_offset=256
'cpu' type_id=8 bits_offset=352
'recent_used_cpu' type_id=8 bits_offset=384
'pid' type_id=9 bits_offset=1056 // pid 在位置 1056
...
CO-RE 支持多種重定位類型:
建立 core_example.bpf.c
:
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_core_read.h>
// 傳統方式 vs CO-RE 方式比較
SEC("tp/sched/sched_process_exec")
int trace_exec_traditional(struct trace_event_raw_sched_process_exec *ctx)
{
struct task_struct *task = (struct task_struct *)bpf_get_current_task();
// 傳統方式:硬編碼偏移量(危險!)
// pid_t pid = *(pid_t *)((char *)task + 1056);
return 0;
}
SEC("tp/sched/sched_process_exec")
int trace_exec_core(struct trace_event_raw_sched_process_exec *ctx)
{
struct task_struct *task = (struct task_struct *)bpf_get_current_task();
// CO-RE 方式:自動重定位
pid_t pid = BPF_CORE_READ(task, pid);
// 檢查欄位是否存在
if (bpf_core_field_exists(task->pid)) {
bpf_printk("PID field exists: %d", pid);
}
// 取得欄位大小
size_t pid_size = bpf_core_field_size(task->pid);
bpf_printk("PID field size: %lu", pid_size);
return 0;
}
char _license[] SEC("license") = "GPL";
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_core_read.h>
SEC("tp/syscalls/sys_enter_openat")
int trace_openat(struct trace_event_raw_sys_enter *ctx)
{
struct task_struct *task = (struct task_struct *)bpf_get_current_task();
// 1. BPF_CORE_READ - 讀取單一欄位
pid_t pid = BPF_CORE_READ(task, pid);
pid_t tgid = BPF_CORE_READ(task, tgid);
// 2. BPF_CORE_READ_STR - 讀取字串
char comm[16];
BPF_CORE_READ_STR(comm, sizeof(comm), task, comm);
// 3. BPF_CORE_READ_INTO - 讀取到指定變數
pid_t local_pid;
BPF_CORE_READ_INTO(&local_pid, task, pid);
// 4. BPF_CORE_READ_USER - 讀取使用者空間資料
char __user *filename = (char __user *)ctx->args[1];
char filename_buf[256];
BPF_CORE_READ_USER_STR(filename_buf, sizeof(filename_buf), filename);
bpf_printk("Process %s (PID: %d, TGID: %d) opening: %s",
comm, pid, tgid, filename_buf);
return 0;
}
SEC("tp/sched/sched_process_fork")
int trace_fork(struct trace_event_raw_sched_process_fork *ctx)
{
struct task_struct *task = (struct task_struct *)bpf_get_current_task();
// 鏈式讀取:task->mm->mmap->vm_file->f_path->dentry->d_name->name
char *exe_name = BPF_CORE_READ(task, mm, exe_file, f_path.dentry, d_name.name);
// 多層指標讀取
struct mm_struct *mm = BPF_CORE_READ(task, mm);
if (mm) {
unsigned long start_brk = BPF_CORE_READ(mm, start_brk);
unsigned long brk = BPF_CORE_READ(mm, brk);
bpf_printk("Process heap: start=0x%lx, current=0x%lx", start_brk, brk);
}
return 0;
}
SEC("tp/sched/sched_process_exit")
int trace_exit(struct trace_event_raw_sched_process_exit *ctx)
{
struct task_struct *task = (struct task_struct *)bpf_get_current_task();
// 檢查欄位是否存在
if (bpf_core_field_exists(task->exit_code)) {
int exit_code = BPF_CORE_READ(task, exit_code);
bpf_printk("Process exit with code: %d", exit_code);
} else {
bpf_printk("exit_code field not available in this kernel");
}
// 取得類型大小
size_t task_size = bpf_core_type_size(struct task_struct);
bpf_printk("task_struct size: %lu bytes", task_size);
// 列舉值處理
if (bpf_core_enum_value_exists(enum pid_type, PIDTYPE_PID)) {
bpf_printk("PIDTYPE_PID value: %d",
bpf_core_enum_value(enum pid_type, PIDTYPE_PID));
}
return 0;
}
建立 process_monitor.h
:
#ifndef __PROCESS_MONITOR_H__
#define __PROCESS_MONITOR_H__
#include <linux/types.h>
#define TASK_COMM_LEN 16
#define MAX_PATH_LEN 256
// 程序事件類型
enum event_type {
EVENT_EXEC = 1,
EVENT_EXIT,
EVENT_FORK,
EVENT_CLONE,
};
// 程序事件結構
struct process_event {
__u64 timestamp;
__u32 event_type;
__u32 pid;
__u32 ppid;
__u32 tid;
__u32 uid;
__u32 gid;
__s32 exit_code;
char comm[TASK_COMM_LEN];
char filename[MAX_PATH_LEN];
} __attribute__((packed));
// 程序統計
struct process_stats {
__u64 total_events;
__u64 exec_count;
__u64 exit_count;
__u64 fork_count;
__u64 clone_count;
} __attribute__((packed));
#endif /* __PROCESS_MONITOR_H__ */
建立 process_monitor.bpf.c
:
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_core_read.h>
#include <bpf/bpf_tracing.h>
#include "process_monitor.h"
// Ring Buffer for events
struct {
__uint(type, BPF_MAP_TYPE_RINGBUF);
__uint(max_entries, 256 * 1024);
} events SEC(".maps");
// Statistics map
struct {
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
__uint(max_entries, 1);
__type(key, __u32);
__type(value, struct process_stats);
} stats_map SEC(".maps");
// 更新統計資訊
static __always_inline void update_stats(enum event_type type)
{
__u32 key = 0;
struct process_stats *stats = bpf_map_lookup_elem(&stats_map, &key);
if (!stats) {
return;
}
__sync_fetch_and_add(&stats->total_events, 1);
switch (type) {
case EVENT_EXEC:
__sync_fetch_and_add(&stats->exec_count, 1);
break;
case EVENT_EXIT:
__sync_fetch_and_add(&stats->exit_count, 1);
break;
case EVENT_FORK:
__sync_fetch_and_add(&stats->fork_count, 1);
break;
case EVENT_CLONE:
__sync_fetch_and_add(&stats->clone_count, 1);
break;
}
}
// 填充基本程序資訊
static __always_inline void fill_process_info(struct process_event *event)
{
struct task_struct *task = (struct task_struct *)bpf_get_current_task();
event->timestamp = bpf_ktime_get_ns();
// 使用 CO-RE 安全讀取
event->pid = BPF_CORE_READ(task, pid);
event->tid = BPF_CORE_READ(task, tgid);
// 讀取父程序 PID
struct task_struct *parent = BPF_CORE_READ(task, parent);
if (parent) {
event->ppid = BPF_CORE_READ(parent, pid);
}
// 讀取使用者資訊
kuid_t uid = BPF_CORE_READ(task, cred, uid);
kgid_t gid = BPF_CORE_READ(task, cred, gid);
event->uid = uid.val;
event->gid = gid.val;
// 讀取程序名稱
BPF_CORE_READ_STR(event->comm, sizeof(event->comm), task, comm);
}
// 取得執行檔路徑
static __always_inline int get_exe_path(char *buf, size_t size)
{
struct task_struct *task = (struct task_struct *)bpf_get_current_task();
// 檢查 mm 是否存在
if (!bpf_core_field_exists(task->mm) || !BPF_CORE_READ(task, mm)) {
return -1;
}
struct mm_struct *mm = BPF_CORE_READ(task, mm);
if (!mm) {
return -1;
}
// 檢查 exe_file 是否存在
if (!bpf_core_field_exists(mm->exe_file)) {
return -1;
}
struct file *exe_file = BPF_CORE_READ(mm, exe_file);
if (!exe_file) {
return -1;
}
// 讀取檔案路徑
struct path path = BPF_CORE_READ(exe_file, f_path);
struct dentry *dentry = BPF_CORE_READ(&path, dentry);
if (dentry) {
struct qstr d_name = BPF_CORE_READ(dentry, d_name);
BPF_CORE_READ_STR(buf, size, d_name.name);
return 0;
}
return -1;
}
// 程序執行事件
SEC("tp/sched/sched_process_exec")
int trace_exec(struct trace_event_raw_sched_process_exec *ctx)
{
struct process_event *event;
// 預留 Ring Buffer 空間
event = bpf_ringbuf_reserve(&events, sizeof(*event), 0);
if (!event) {
return 0;
}
// 初始化事件
__builtin_memset(event, 0, sizeof(*event));
event->event_type = EVENT_EXEC;
// 填充基本資訊
fill_process_info(event);
// 取得執行檔路徑
if (get_exe_path(event->filename, sizeof(event->filename)) != 0) {
// 如果無法取得路徑,使用程序名稱
__builtin_memcpy(event->filename, event->comm, sizeof(event->comm));
}
// 提交事件
bpf_ringbuf_submit(event, 0);
// 更新統計
update_stats(EVENT_EXEC);
return 0;
}
// 程序結束事件
SEC("tp/sched/sched_process_exit")
int trace_exit(struct trace_event_raw_sched_process_exit *ctx)
{
struct process_event *event;
event = bpf_ringbuf_reserve(&events, sizeof(*event), 0);
if (!event) {
return 0;
}
__builtin_memset(event, 0, sizeof(*event));
event->event_type = EVENT_EXIT;
fill_process_info(event);
// 讀取退出代碼
if (bpf_core_field_exists(((struct task_struct *)0)->exit_code)) {
struct task_struct *task = (struct task_struct *)bpf_get_current_task();
event->exit_code = BPF_CORE_READ(task, exit_code);
}
bpf_ringbuf_submit(event, 0);
update_stats(EVENT_EXIT);
return 0;
}
// 程序 fork 事件
SEC("tp/sched/sched_process_fork")
int trace_fork(struct trace_event_raw_sched_process_fork *ctx)
{
struct process_event *event;
event = bpf_ringbuf_reserve(&events, sizeof(*event), 0);
if (!event) {
return 0;
}
__builtin_memset(event, 0, sizeof(*event));
event->event_type = EVENT_FORK;
fill_process_info(event);
bpf_ringbuf_submit(event, 0);
update_stats(EVENT_FORK);
return 0;
}
// kprobe 範例:監控 do_exit
SEC("kprobe/do_exit")
int kprobe_do_exit(struct pt_regs *ctx)
{
struct task_struct *task = (struct task_struct *)bpf_get_current_task();
pid_t pid = BPF_CORE_READ(task, pid);
pid_t tgid = BPF_CORE_READ(task, tgid);
// 取得退出代碼參數
long exit_code = PT_REGS_PARM1(ctx);
bpf_printk("Process %d (tgid: %d) exiting with code: %ld",
pid, tgid, exit_code);
return 0;
}
char _license[] SEC("license") = "GPL";
建立 main.go
:
package main
import (
"bytes"
"encoding/binary"
"fmt"
"log"
"os"
"os/signal"
"syscall"
"time"
"unsafe"
"github.com/aquasecurity/libbpfgo"
)
// 對應 C 結構體
type ProcessEvent struct {
Timestamp uint64
EventType uint32
PID uint32
PPID uint32
TID uint32
UID uint32
GID uint32
ExitCode int32
Comm [16]byte
Filename [256]byte
}
type ProcessStats struct {
TotalEvents uint64
ExecCount uint64
ExitCount uint64
ForkCount uint64
CloneCount uint64
}
// 事件類型常數
const (
EventExec = 1
EventExit = 2
EventFork = 3
EventClone = 4
)
type ProcessMonitor struct {
module *libbpfgo.Module
rb *libbpfgo.RingBuffer
}
func NewProcessMonitor(objPath string) (*ProcessMonitor, error) {
// 載入 eBPF 程式
module, err := libbpfgo.NewModuleFromFile(objPath)
if err != nil {
return nil, fmt.Errorf("failed to load BPF object: %v", err)
}
if err := module.BPFLoadObject(); err != nil {
return nil, fmt.Errorf("failed to load BPF object: %v", err)
}
// 附加 tracepoint 程式
execProg, err := module.GetProgram("trace_exec")
if err != nil {
return nil, fmt.Errorf("failed to get exec program: %v", err)
}
exitProg, err := module.GetProgram("trace_exit")
if err != nil {
return nil, fmt.Errorf("failed to get exit program: %v", err)
}
forkProg, err := module.GetProgram("trace_fork")
if err != nil {
return nil, fmt.Errorf("failed to get fork program: %v", err)
}
kprobeProg, err := module.GetProgram("kprobe_do_exit")
if err != nil {
return nil, fmt.Errorf("failed to get kprobe program: %v", err)
}
// 附加程式
if _, err := execProg.AttachTracepoint("sched", "sched_process_exec"); err != nil {
return nil, fmt.Errorf("failed to attach exec tracepoint: %v", err)
}
if _, err := exitProg.AttachTracepoint("sched", "sched_process_exit"); err != nil {
return nil, fmt.Errorf("failed to attach exit tracepoint: %v", err)
}
if _, err := forkProg.AttachTracepoint("sched", "sched_process_fork"); err != nil {
return nil, fmt.Errorf("failed to attach fork tracepoint: %v", err)
}
if _, err := kprobeProg.AttachKprobe("do_exit"); err != nil {
log.Printf("Warning: failed to attach kprobe: %v", err)
}
return &ProcessMonitor{
module: module,
}, nil
}
func (pm *ProcessMonitor) StartMonitoring() error {
// 取得 Ring Buffer
eventsMap, err := pm.module.GetMap("events")
if err != nil {
return fmt.Errorf("failed to get events map: %v", err)
}
pm.rb, err = pm.module.InitRingBuf("events", pm.handleEvent)
if err != nil {
return fmt.Errorf("failed to init ring buffer: %v", err)
}
// 開始處理事件
pm.rb.Start()
return nil
}
func (pm *ProcessMonitor) handleEvent(data []byte) {
// 解析事件
event := (*ProcessEvent)(unsafe.Pointer(&data[0]))
// 轉換字串
comm := nullTerminatedString(event.Comm[:])
filename := nullTerminatedString(event.Filename[:])
// 格式化時間
timestamp := time.Unix(0, int64(event.Timestamp))
// 格式化事件類型
eventTypeStr := map[uint32]string{
EventExec: "EXEC",
EventExit: "EXIT",
EventFork: "FORK",
EventClone: "CLONE",
}[event.EventType]
// 輸出事件
fmt.Printf("[%s] %s: PID=%d PPID=%d TID=%d UID=%d GID=%d",
timestamp.Format("15:04:05.000"),
eventTypeStr,
event.PID,
event.PPID,
event.TID,
event.UID,
event.GID)
if event.EventType == EventExit {
fmt.Printf(" ExitCode=%d", event.ExitCode)
}
fmt.Printf(" Comm=%s", comm)
if filename != "" && filename != comm {
fmt.Printf(" File=%s", filename)
}
fmt.Println()
}
func (pm *ProcessMonitor) GetStats() (*ProcessStats, error) {
statsMap, err := pm.module.GetMap("stats_map")
if err != nil {
return nil, fmt.Errorf("failed to get stats map: %v", err)
}
key := uint32(0)
value, err := statsMap.GetValue(unsafe.Pointer(&key))
if err != nil {
return nil, fmt.Errorf("failed to get stats: %v", err)
}
// 處理 Per-CPU 統計
stats := &ProcessStats{}
cpuCount := len(value) / int(unsafe.Sizeof(*stats))
for i := 0; i < cpuCount; i++ {
offset := i * int(unsafe.Sizeof(*stats))
cpuStats := (*ProcessStats)(unsafe.Pointer(&value[offset]))
stats.TotalEvents += cpuStats.TotalEvents
stats.ExecCount += cpuStats.ExecCount
stats.ExitCount += cpuStats.ExitCount
stats.ForkCount += cpuStats.ForkCount
stats.CloneCount += cpuStats.CloneCount
}
return stats, nil
}
func (pm *ProcessMonitor) PrintStats() {
stats, err := pm.GetStats()
if err != nil {
log.Printf("Failed to get stats: %v", err)
return
}
fmt.Println("\n=== Process Monitor Statistics ===")
fmt.Printf("Total Events: %d\n", stats.TotalEvents)
fmt.Printf("Exec Events: %d\n", stats.ExecCount)
fmt.Printf("Exit Events: %d\n", stats.ExitCount)
fmt.Printf("Fork Events: %d\n", stats.ForkCount)
fmt.Printf("Clone Events: %d\n", stats.CloneCount)
}
func (pm *ProcessMonitor) Close() {
if pm.rb != nil {
pm.rb.Stop()
pm.rb.Close()
}
if pm.module != nil {
pm.module.Close()
}
}
func nullTerminatedString(b []byte) string {
if i := bytes.IndexByte(b, 0); i >= 0 {
return string(b[:i])
}
return string(b)
}
func main() {
// 建立程序監控器
monitor, err := NewProcessMonitor("process_monitor.bpf.o")
if err != nil {
log.Fatalf("Failed to create process monitor: %v", err)
}
defer monitor.Close()
// 開始監控
if err := monitor.StartMonitoring(); err != nil {
log.Fatalf("Failed to start monitoring: %v", err)
}
fmt.Println("Process Monitor started. Press Ctrl+C to stop...")
fmt.Println("Monitoring process exec, exit, and fork events...")
// 設定信號處理
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
// 定期顯示統計資訊
ticker := time.NewTicker(30 * time.Second)
defer ticker.Stop()
for {
select {
case <-ticker.C:
monitor.PrintStats()
case <-sigChan:
fmt.Println("\nShutting down...")
monitor.PrintStats()
return
}
}
}
建立 Makefile
:
# Process Monitor Makefile
CLANG ?= clang
LLVM_STRIP ?= llvm_strip
ARCH := x86_64
# 輸出檔案
BPF_OBJ = process_monitor.bpf.o
TARGET = process_monitor
# 編譯標誌
CFLAGS := -O2 -g -Wall -Werror
BPF_CFLAGS := -target bpf -D__TARGET_ARCH_$(ARCH)
# 包含路徑
INCLUDES := -I/usr/include/$(shell uname -m)-linux-gnu -I. -I./vmlinux
.PHONY: all clean test vmlinux
all: vmlinux $(BPF_OBJ) $(TARGET)
# 生成 vmlinux.h
vmlinux:
mkdir -p vmlinux
bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux/vmlinux.h
# 編譯 eBPF 程式
$(BPF_OBJ): process_monitor.bpf.c process_monitor.h
$(CLANG) $(BPF_CFLAGS) $(INCLUDES) $(CFLAGS) -c $< -o $@
$(LLVM_STRIP) -g $@
# 編譯 Go 程式
$(TARGET): main.go $(BPF_OBJ)
go mod init process_monitor || true
go get github.com/aquasecurity/libbpfgo
go build -o $(TARGET) main.go
# 測試
test: $(TARGET)
@echo "Testing process monitor..."
@echo "Run: sudo ./$(TARGET)"
@echo "In another terminal, run some commands to generate events"
# 檢查 BTF 支援
check-btf:
@if [ ! -f /sys/kernel/btf/vmlinux ]; then \
echo "ERROR: BTF not supported on this kernel"; \
echo "Enable CONFIG_DEBUG_INFO_BTF=y in kernel config"; \
exit 1; \
fi
@echo "BTF support: OK"
# 清理
clean:
rm -f $(BPF_OBJ) $(TARGET)
rm -rf vmlinux/
rm -f go.mod go.sum
help:
@echo "Available targets:"
@echo " all - Build all components"
@echo " vmlinux - Generate vmlinux.h"
@echo " test - Run test"
@echo " check-btf - Check BTF support"
@echo " clean - Clean all artifacts"
@echo " help - Show this help"
# 檢查 BTF 支援
make check-btf
# 編譯程式
make all
# 執行監控器
sudo ./process_monitor
在另一個終端執行:
# 產生不同類型的事件
ls /tmp # exec 事件
sleep 1 # exec + exit 事件
bash -c "echo hello" # fork + exec + exit 事件
python3 -c "print('hi')" # exec + exit 事件
// 總是檢查欄位是否存在
if (bpf_core_field_exists(task->some_field)) {
value = BPF_CORE_READ(task, some_field);
} else {
// 提供備選方案或跳過
bpf_printk("Field not available in this kernel version");
}
// 使用條件編譯處理不同核心版本
#if __has_builtin(__builtin_preserve_access_index)
#define BPF_CORE_READ_BITFIELD_PROBED(dst, src) \
__builtin_preserve_access_index(({dst = src;}))
#else
#define BPF_CORE_READ_BITFIELD_PROBED(dst, src) \
bpf_probe_read(&dst, sizeof(dst), &src)
#endif
// 快取常用的資料結構大小
static const size_t task_struct_size = bpf_core_type_size(struct task_struct);
// 使用編譯時常數
#define PID_OFFSET bpf_core_field_offset(struct task_struct, pid)
// 除錯資訊
#ifdef DEBUG
bpf_printk("Field offset: %d, size: %d",
bpf_core_field_offset(struct task_struct, pid),
bpf_core_field_size(struct task_struct, pid));
#endif
# 檢查 BTF 支援
ls /sys/kernel/btf/vmlinux
# 如果不存在,檢查核心配置
zcat /proc/config.gz | grep BTF
// 確保包含正確的標頭檔
#include "vmlinux.h" // 必須在其他 include 之前
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_core_read.h>
# 檢查 BTF 資訊
bpftool btf dump file /sys/kernel/btf/vmlinux | grep "struct task_struct"
# 檢查程式載入資訊
bpftool prog load process_monitor.bpf.o /sys/fs/bpf/test
// 自定義存取巨集
#define CORE_READ_TASK_FIELD(task, field) ({ \
typeof(((struct task_struct *)0)->field) __val; \
BPF_CORE_READ_INTO(&__val, task, field); \
__val; \
})
// 架構特定的處理
#if defined(__TARGET_ARCH_x86)
#define ARCH_SPECIFIC_OFFSET 8
#elif defined(__TARGET_ARCH_arm64)
#define ARCH_SPECIFIC_OFFSET 16
#else
#define ARCH_SPECIFIC_OFFSET 0
#endif
// 核心版本檢查
#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,8,0)
// 新版本特性
#else
// 舊版本相容性處理
#endif
通過本篇文章,我們深入學習了 CO-RE 技術:
CO-RE 技術是現代 eBPF 開發的基石,讓我們能夠編寫真正可移植的 eBPF 程式。