mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4
2303 字
7 分钟
eBPF 与内存管理
2026-03-21

eBPF 程序运行在内核中,它的内存使用直接影响系统稳定性——Map 太大可能耗尽内核内存,栈溢出会导致验证器拒绝,容器中的 eBPF 内存可能被 cgroup 误计费。理解 eBPF 的内存模型,是生产部署的必修课。

一、eBPF 内存模型#

1.1 eBPF 的内存组成#

flowchart TB subgraph eBPF内存["eBPF 内存组成"] PROG["程序代码<br/>JIT 编译后的本机码"] MAP["Map 数据<br/>键值存储"] STACK["栈空间<br/>512 字节/程序调用"] CTX["上下文<br/>ctx 结构体"] end PROG -->|"大小 = 指令数 × 指令长度"| SIZE1["通常 1-100 KB"] MAP -->|"大小 = max_entries × (key + value)"| SIZE2["通常 1-1000 MB"] STACK -->|"固定 512 字节"| SIZE3["512 B"] CTX -->|"由内核分配"| SIZE4["可忽略"] style MAP fill:#ffcdd2,stroke:#c62828

1.2 内存限制#

资源限制说明
栈空间512 字节每次函数调用
指令数100 万单个程序
Map 条目max_entries创建时指定
Map 内存受 RLIMIT_MEMLOCK 限制5.11+ 改为 cgroup
尾调用深度8 层防止无限递归

二、Map 内存开销#

2.1 内存计算公式#

Hash Map 内存 ≈ max_entries × (key_size + value_size + sizeof(struct htab_elem))
Array Map 内存 ≈ max_entries × value_size
Per-CPU Map 内存 ≈ CPU 数 × max_entries × value_size
Ring Buffer 内存 = max_entries(由用户指定)

2.2 实际内存开销#

Map 配置内存开销
HASH, key=4B, value=64B, max=10000~1 MB
HASH, key=4B, value=64B, max=100000~10 MB
LRU_HASH, key=4B, value=64B, max=100000~15 MB
PERCPU_HASH, key=4B, value=64B, max=10000, 64 CPUs~64 MB
ARRAY, value=64B, max=10000~640 KB
RINGBUF, max=256KB256 KB

2.3 内存优化策略#

// 优化 1:使用 BPF_F_NO_PREALLOC 避免预分配
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 1000000);
__uint(map_flags, BPF_F_NO_PREALLOC); // 按需分配
__type(key, u32);
__type(value, struct event);
} large_map SEC(".maps");
// 优化 2:使用 LRU 替代无限增长
struct {
__uint(type, BPF_MAP_TYPE_LRU_HASH);
__uint(max_entries, 100000); // 自动淘汰
__type(key, struct conn_key);
__type(value, struct conn_value);
} conn_track SEC(".maps");
// 优化 3:减小 value 大小
// 浪费空间
struct big_value {
char filename[256]; // 大部分时候用不到 256 字节
u64 timestamp;
};
// 紧凑存储
struct compact_value {
char filename[64]; // 按需截断
u64 timestamp;
};
Warning

BPF_F_NO_PREALLOC 虽然节省内存,但在内存紧张时 bpf_map_update_elem() 可能失败(返回 -ENOMEM)。对于关键路径(如 XDP),建议使用预分配模式。

2.4 栈空间与尾调用优化#

eBPF 程序的栈空间只有 512 字节,这是验证器强制的安全限制。在复杂程序中,512 字节很容易不够用——几个局部数组或嵌套结构体就会耗尽栈空间,导致验证器拒绝加载。

常见的栈溢出模式:

// 栈溢出:两个数组就超过 512 字节
SEC("kprobe/do_sys_open")
int trace_open(struct pt_regs *ctx)
{
char filename[256]; // 256 字节
char comm[256]; // 256 字节 → 合计 512 字节,加上其他局部变量必溢出
// 验证器报错:combined stack size of 4 calls is 528. Too large
}

尾调用(tail call)是解决栈溢出的核心手段:通过 bpf_tail_call() 将当前栈帧替换为新程序的栈帧,实现”零栈增长”的程序串联:

// 使用尾调用拆分逻辑,每个子程序独立使用 512 字节栈
struct {
__uint(type, BPF_MAP_TYPE_PROG_ARRAY);
__uint(max_entries, 4);
__type(key, u32);
__type(value, u32);
} prog_array SEC(".maps");
// 阶段 1:解析数据包头
SEC("xdp")
int xdp_parse(struct xdp_md *ctx)
{
// 只做解析,栈占用少
struct hdr_info info = parse_header(ctx);
// 尾调用到阶段 2,当前栈帧被替换
bpf_tail_call(ctx, &prog_array, 1);
return XDP_PASS;
}
// 阶段 2:执行过滤逻辑(独立 512 字节栈)
SEC("xdp")
int xdp_filter(struct xdp_md *ctx)
{
// 可以使用新的 512 字节栈空间
char payload[256];
// ...
return XDP_DROP;
}

当尾调用也不够时,Per-CPU Array Map 可以充当”临时暂存区”——将大型中间数据存入 Per-CPU Map,避免占用栈空间:

// 用 Per-CPU Array 作为 scratch 空间
struct scratch_space {
char buf[256];
u32 len;
u32 flags;
};
struct {
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
__uint(max_entries, 1);
__type(key, u32);
__type(value, struct scratch_space);
} scratch SEC(".maps");
SEC("kprobe/do_sys_open")
int trace_open(struct pt_regs *ctx)
{
u32 key = 0;
struct scratch_space *sp = bpf_map_lookup_elem(&scratch, &key);
if (!sp) return 0;
// 使用 sp->buf 代替栈上的局部数组
bpf_probe_read_user_str(sp->buf, sizeof(sp->buf), (void *)PT_REGS_PARM2(ctx));
sp->len = __builtin_strlen(sp->buf);
// ...
return 0;
}
栈溢出解决方案原理限制
尾调用替换栈帧,每个子程序独立 512B最多 8 层嵌套
Per-CPU ArrayMap 充当 scratch 空间value 大小 ≤ 4KB(页面大小)
减小局部变量压缩结构体、缩短数组可能影响可读性
拆分为多个程序逻辑拆分到独立程序程序间无法共享栈变量

三、容器内存与 eBPF#

3.1 eBPF 内存的 cgroup 计费#

在容器环境中,eBPF 内存的计费方式经历了重要变化:

内核版本计费方式影响
< 5.11RLIMIT_MEMLOCK全局限额,不与 cgroup 关联
5.11+cgroup 内存控制器eBPF 内存计入 cgroup
5.15+独立 memcg 计费eBPF 内存独立于容器限额

3.2 容器中运行 eBPF 的内存问题#

flowchart TB subgraph 问题场景 CONTAINER["容器<br/>memory.limit = 512MB"] EBPF["eBPF Map<br/>占用 200MB"] APP["应用<br/>需要 400MB"] end CONTAINER -->|"200MB + 400MB > 512MB"| OOM["OOM Kill "] style OOM fill:#ffcdd2,stroke:#c62828

3.3 解决方案#

  1. 使用 CAP_BPF 而非 root:5.8+ 内核支持 CAP_BPF,减少权限需求
  2. 在 init 容器中加载 eBPF:避免 eBPF 内存计入应用容器
  3. 使用独立 DaemonSet:eBPF 程序在独立 Pod 中运行
  4. 调整 memory.limit:为 eBPF 内存预留足够空间
# Cilium DaemonSet:独立 Pod 运行 eBPF
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: cilium
spec:
template:
spec:
containers:
- name: cilium-agent
resources:
requests:
memory: "512Mi"
limits:
memory: "1Gi" # 预留 eBPF 内存

四、eBPF 内存追踪#

4.1 追踪内核内存分配#

#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
struct alloc_event {
u32 pid;
u64 size;
u64 addr;
char comm[16];
};
struct {
__uint(type, BPF_MAP_TYPE_RINGBUF);
__uint(max_entries, 256 * 1024);
} events SEC(".maps");
// 追踪 kmalloc
SEC("kprobe/kmalloc")
int trace_kmalloc(struct pt_regs *ctx)
{
struct alloc_event *e;
e = bpf_ringbuf_reserve(&events, sizeof(*e), 0);
if (!e) return 0;
e->pid = bpf_get_current_pid_tgid() >> 32;
e->size = PT_REGS_PARM1(ctx); // size 参数
bpf_get_current_comm(&e->comm, sizeof(e->comm));
bpf_ringbuf_submit(e, 0);
return 0;
}
char LICENSE[] SEC("license") = "GPL";

4.2 追踪容器内存使用#

# 使用 bpftrace 追踪容器内存分配
sudo bpftrace -e '
kprobe:kmalloc /pid == $TARGET_PID/ {
@alloc_size[comm] = hist(arg1);
}'
# 追踪页面分配
sudo bpftrace -e '
kprobe:alloc_pages {
@alloc[comm] = count();
}'
# 追踪 cgroup 内存事件
sudo bpftrace -e '
kprobe:mem_cgroup_charge {
printf("cgroup charge: %d bytes\n", arg1);
}'

五、eBPF-mm 子系统#

5.1 eBPF-mm 的功能#

eBPF-mm 是 Linux 内核中 eBPF 与内存管理子系统的集成,允许 eBPF 程序参与内存管理决策:

功能内核版本说明
bpf_kptr5.16Map 中存储内核指针
bpf_arena6.xeBPF 程序的共享内存区域
bpf_timer5.15Map 中的定时器
bpf_mem_cache6.xeBPF 专用内存缓存

5.2 bpf_kptr:Map 中的内核指针#

// 在 Map value 中存储内核指针
struct value_with_kptr {
int data;
struct task_struct __kptr *task; // 内核指针
};
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 1024);
__type(key, u32);
__type(value, struct value_with_kptr);
} kptr_map SEC(".maps");

5.3 bpf_arena:共享内存区域#

bpf_arena 是 Linux 6.9+ 引入的共享内存机制,允许多个 eBPF 程序共享一块可读写的内存区域。与 Map 不同,arena 提供的是指针级访问——eBPF 程序可以直接通过指针读写 arena 中的数据,无需 bpf_map_lookup_elem() 的间接调用。

// 定义 arena(内核 6.9+)
struct {
__uint(type, BPF_MAP_TYPE_ARENA);
__uint(map_flags, BPF_F_MMAPABLE);
__uint(max_entries, 4096); // 4096 页 = 16 MB
__uint(value_size, 4096); // 页大小
} arena SEC(".maps");
// 在 eBPF 程序中通过指针访问 arena
SEC("kprobe/do_sys_open")
int trace_open(struct pt_regs *ctx)
{
// arena 中的数据可跨程序共享
int *counter = (int *)arena + 0; // arena 基址偏移
__sync_fetch_and_add(counter, 1);
return 0;
}
特性bpf_arena普通 Map
访问方式指针直接读写bpf_map_lookup_elem()
共享范围同一 arena 的所有程序同一 Map 的所有程序
数据结构自定义(链表/树等)固定键值对
内存开销按页分配按 entry 预分配
内核版本6.9+4.4+
Note

bpf_arena 目前仍处于早期阶段,适用于需要在多个 eBPF 程序间共享复杂数据结构(如链表、红黑树)的场景。对于简单的键值存储,普通 Map 仍然是更好的选择。

5.4 bpf_timer 与 bpf_mem_cache#

bpf_timer 允许 eBPF 程序设置定时器回调,在指定时间后执行内核态逻辑——无需用户态轮询。定时器回调中的内存分配由内核自动管理:

struct timer_value {
struct bpf_timer timer;
u32 callback_count;
};
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 100);
__type(key, u32);
__type(value, struct timer_value);
} timer_map SEC(".maps);
// 定时器回调:定期清理过期条目
static int timer_callback(void *map, u32 *key, struct timer_value *val)
{
val->callback_count++;
// 执行清理逻辑...
// 重新设置定时器(周期执行)
bpf_timer_start(&val->timer, timer_callback, 1000000000ULL); // 1 秒
return 0;
}
SEC("tc")
int setup_timer(struct __sk_buff *ctx)
{
u32 key = 1;
struct timer_value *val = bpf_map_lookup_elem(&timer_map, &key);
if (val && val->callback_count == 0) {
bpf_timer_init(&val->timer, &timer_map, CLOCK_MONOTONIC);
bpf_timer_start(&val->timer, timer_callback, 1000000000ULL);
}
return TC_ACT_OK;
}

bpf_mem_cache 为 eBPF 程序提供专用的内存缓存分配器,避免在热路径上触发内核通用分配器(slab allocator)的锁竞争:

// bpf_mem_cache 使用示例(内核 6.x+)
// 在定时器回调或尾调用中分配临时内存
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 1024);
__type(key, u32);
__type(value, struct bpf_mem_cache_ptr);
} mem_cache SEC(".maps");
机制用途内存来源内核版本
bpf_timer延迟/周期执行Map value 内嵌5.15+
bpf_mem_cache热路径内存分配eBPF 专用 slab6.x+
bpf_kptrMap 中存内核对象引用计数管理5.16+
bpf_arena共享内存区域按页映射6.9+

六、eBPF 内存生命周期#

6.1 分配与释放时机#

eBPF 内存的分配和释放遵循明确的生命周期规则,理解这些规则才能避免内存泄漏和悬垂引用:

flowchart TB LOAD["bpf() 系统调用<br/>加载程序/创建 Map"] --> ALLOC["内核分配内存<br/>程序代码 + Map 数据"] ALLOC --> ACTIVE["活跃状态<br/>程序运行 / Map 可读写"] ACTIVE -->|"pin 到 BPF 文件系统"| PINNED["Pinned 状态<br/>引用计数 > 0"] ACTIVE -->|"fd 关闭且未 pin"| FREE["内核释放内存"] PINNED -->|"unpin 或进程退出"| FREE style ALLOC fill:#c8e6c9,stroke:#2e7d32 style PINNED fill:#fff9c4,stroke:#f9a825 style FREE fill:#ffcdd2,stroke:#c62828

关键规则:

  1. Map 生命周期:Map 的 fd 关闭后,如果没有被 pin 且没有程序引用它,内核自动释放
  2. 程序生命周期:程序 detach 后,如果没有被 pin 且没有 Map 引用,内核自动释放
  3. Pinning:通过 bpf_obj_pin() 将 Map/程序钉在 /sys/fs/bpf/ 下,即使创建进程退出也不会释放
  4. 引用计数:bpf_kptr、bpf_timer 等机制通过引用计数管理对象生命周期

6.2 引用计数与内存回收#

对象引用计数触发释放条件
Mapfd 引用 + pinfd 关闭 + 未 pin + 无程序引用
程序fd 引用 + pin + attachfd 关闭 + 未 pin + 未 attach
bpf_kptrbpf_kptr_xchg()引用计数归零
bpf_timerMap value 内嵌随 Map 释放
Ring Buffer 事件bpf_ringbuf_reserve()bpf_ringbuf_submit()bpf_ringbuf_discard()

七、RLIMIT_MEMLOCK 到 cgroup 迁移#

7.1 传统 RLIMIT_MEMLOCK 的局限#

5.11 之前的内核使用 RLIMIT_MEMLOCK 控制 eBPF 内存限额——这是一个进程级的全局限额,所有 eBPF Map 共享同一个配额:

# 查看当前 RLIMIT_MEMLOCK
ulimit -l
# 64 ← 单位 KB,即 64KB(默认值太小)
# 临时调大(开发环境常用)
ulimit -l unlimited
# 永久设置(/etc/security/limits.conf)
# * hard memlock unlimited
# * soft memlock unlimited

RLIMIT_MEMLOCK 的问题:

问题说明
全局限额所有 eBPF Map 共享,一个程序可能耗尽配额
不与 cgroup 关联容器内存限额无法控制 eBPF 内存
难以调试配额耗尽时错误信息不明确
不支持动态调整需要重启进程才能修改

7.2 cgroup 内存计费(5.11+)#

5.11 开始,eBPF 内存计入创建进程所属的 cgroup,实现了与容器限额的统一管理:

// libbpf 自动处理 RLIMIT_MEMLOCK → cgroup 的迁移
// 用户态代码无需修改,libbpf 1.0+ 会自动尝试 cgroup 计费
// 手动控制(高级场景)
struct bpf_object_open_opts opts = {
.sz = sizeof(opts),
// libbpf 1.0+ 默认使用 cgroup 计费
// 如需回退到 RLIMIT_MEMLOCK,设置:
// .btf_custom_path = NULL,
};
# 检查内核是否支持 cgroup eBPF 计费
cat /proc/config.gz | gunzip | grep CONFIG_MEMCG_KMEM
# CONFIG_MEMCG_KMEM=y ← 需要开启
# 查看 cgroup 中的 eBPF 内存
cat /sys/fs/cgroup/memory.stat | grep -i bpf
Tip

升级到 5.11+ 内核后,如果 eBPF 程序加载失败,检查 cgroup 内存限额是否足够——之前 RLIMIT_MEMLOCK 设为 unlimited 时可能掩盖了真实的内存需求。

八、内存优化最佳实践#

8.1 优化清单#

优化效果适用场景
使用 LRU Hash限制 Map 大小连接跟踪
使用 Per-CPU Map避免锁开销高频计数器
使用 BPF_F_NO_PREALLOC按需分配大 Map 低命中率
减小 value 大小减少内存占用所有场景
使用 Ring Buffer替代 Perf Buffer事件传输
合理设置 max_entries避免过度预分配所有场景

8.2 监控 eBPF 内存使用#

# 查看所有 Map 的内存使用
sudo bpftool map show
# 查看特定 Map 的统计
sudo bpftool map show id 123
# 查看系统 eBPF 内存总量
cat /proc/meminfo | grep -i bpf
# 使用 bpftrace 追踪 Map 操作
sudo bpftrace -e '
kprobe:bpf_map_update_elem {
@map_ops[comm] = count();
}'
# 追踪内存分配延迟
sudo bpftrace -e '
kprobe:kmalloc /pid == $TARGET/ {
@ts[tid] = nsecs;
}
kretprobe:kmalloc /@ts[tid]/ {
@latency = hist(nsecs - @ts[tid]);
delete(@ts[tid]);
}'
# 追踪 slab 分配器行为
sudo bpftrace -e '
kprobe:kmem_cache_alloc {
@slab[comm, arg0] = count();
}'

九、生产案例:容器 OOM 排查#

9.1 问题场景#

某生产环境 K8s 集群中,运行可观测性 Agent 的 Pod 频繁被 OOM Kill。Pod 的内存限额为 512Mi,应用自身内存使用约 300Mi,但 cgroup 统计显示内存使用量持续增长至限额。

9.2 排查过程#

# 第 1 步:查看 OOM 事件
dmesg | grep -i oom
# Out of memory: Killed process 12345 (agent) total-vm:800MB, anon-rss:500MB
# 第 2 步:检查 eBPF Map 内存
sudo bpftool map show
# 发现:一个 HASH Map 占用 200MB(max_entries=1000000, value_size=200)
# 第 3 步:确认 eBPF 内存计入容器 cgroup
cat /sys/fs/cgroup/memory/kubepods/burstable/pod<id>/memory.usage_in_bytes
# 536870912 ← 512MB,已到限额
# 第 4 步:查看 Map 实际使用率
sudo bpftool map dump id 123 | wc -l
# 实际只有 50000 条目,但预分配了 1000000 的空间

9.3 根因与修复#

根因:Agent 在容器内创建了一个 max_entries=1000000 的 Hash Map,预分配约 200MB 内存。由于 5.11+ 内核将 eBPF 内存计入 cgroup,这 200MB 被算入容器的 512MB 限额。

// 修复前:过度预分配
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 1000000); // 预分配 ~200MB
__type(key, struct conn_key);
__type(value, struct conn_value);
} conn_track SEC(".maps");
// 修复后:使用 LRU + 合理容量
struct {
__uint(type, BPF_MAP_TYPE_LRU_HASH);
__uint(max_entries, 100000); // 预分配 ~20MB,自动淘汰
__type(key, struct conn_key);
__type(value, struct conn_value);
} conn_track SEC(".maps");
Warning

在容器中运行 eBPF 程序时,务必将 eBPF Map 的预分配内存纳入容器内存限额的计算中。一个 max_entries=1000000 的 Hash Map 可能占用数百 MB,轻松耗尽容器的内存限额。

十、动手实践#

10.1 计算 Map 内存开销#

# 创建不同大小的 Map 并观察内存
sudo bpftool map create /sys/fs/bpf/test_hash \
type hash key 4 value 64 entries 100000
# 查看 Map 内存
sudo bpftool map show pinned /sys/fs/bpf/test_hash
# 输出包含:bytes_used
# 清理
sudo rm /sys/fs/bpf/test_hash

10.2 追踪内存分配#

# 使用 bpftrace 追踪 kmalloc 大小分布
sudo bpftrace -e '
kprobe:kmalloc {
@size[comm] = hist(arg0);
}
interval:s:10 {
print(@size);
clear(@size);
}'

10.3 监控容器中的 eBPF 内存#

# 查看 cgroup 内存使用
cat /sys/fs/cgroup/memory/docker/<container_id>/memory.usage_in_bytes
# 查看 eBPF 程序内存
sudo bpftool prog show
# 查看 Map 内存
sudo bpftool map show
graph TB ALLOC["eBPF Map 分配"] --> TYPE{"Map 类型?"} TYPE -->|"Hash/LRU"| KMALLOC["kmalloc<br/>内核堆"] TYPE -->|"Per-CPU"| PERCPU["per-cpu 内存<br/>无锁访问"] TYPE -->|"Ring Buffer"| RBMEM["Ring Buffer 页<br/>mmap 共享"] TYPE -->|"Arena"| ARENA["bpf_arena<br/>共享内存区"] style PERCPU fill:#c8e6c9,stroke:#2e7d32 style RBMEM fill:#fff9c4,stroke:#f9a825

十一、本章小结#

上一章理解了eBPF 在 Kubernetes 中的应用。

本章详解了 eBPF 的内存管理:

主题关键要点
内存组成程序代码 + Map 数据 + 512B 栈 + 上下文
Map 内存Hash/Array/Per-CPU/Ring Buffer 的计算公式与优化策略
栈空间512B 限制,尾调用和 Per-CPU Array 解决溢出
容器内存cgroup 计费(5.11+)、独立 DaemonSet、init 容器加载
内存追踪bpftrace 追踪 kmalloc/slab/延迟分布
eBPF-mmbpf_kptr(内核指针)、bpf_arena(共享内存)、bpf_timer(定时器)、bpf_mem_cache(专用缓存)
内存生命周期分配→活跃→pin→释放,引用计数管理
RLIMIT→cgroup5.11+ 迁移到 cgroup 计费,libbpf 自动处理
生产案例容器 OOM 因 eBPF Map 预分配,改用 LRU Hash 修复

支持与分享

如果这篇文章对你有帮助,欢迎支持作者或分享给更多人

eBPF 与内存管理
https://blog.souloss.com/posts/ebpf/ebpf-memory-management/
作者
Souloss
发布于
2026-03-21
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时

相关文章 智能推荐
1
eBPF 与 WebAssembly
eBPF eBPF 提供内核可编程能力,WebAssembly 提供跨平台可移植性——两者的融合会带来什么?本章详解 Wasm-eBPF 项目、用户态 eBPF 运行时、eBPF 程序的 Wasm 封装,以及 eBPF + Wasm 在边缘计算、插件系统、跨平台可观测性中的应用前景。
2
TC:流量控制与 eBPF
eBPF TC(Traffic Control)是 Linux 内核的流量控制子系统,通过 cls_bpf 分类器可以在 TC 层挂载 eBPF 程序,实现灵活的数据包分类、修改和重定向。本章详解 TC eBPF 的架构、ingress/egress 双向处理、direct action 模式、sk_buff 操作,以及 TC 与 XDP 的选择策略。
3
eBPF 可观测性
eBPF eBPF 最大的应用场景是可观测性——零侵入、低开销、内核级的全链路追踪。本章详解三大可观测性工具链——bpftrace(一行命令追踪内核)、BCC(Python 前端 + 丰富工具集)、Beyla(零侵入应用性能监控),并通过实战展示性能分析、分布式追踪、应用性能监控的完整工作流。
4
eBPF 生产部署
eBPF eBPF 生产部署实践——性能开销分析、调试技巧、版本兼容、内核版本要求与升级策略。
5
eBPF Hook 点:kprobe/tracepoint/uprobe
eBPF eBPF 程序的价值在于它能挂载到内核的各种检查点——Hook 点。本章详解三大 Hook 机制——kprobe(动态内核函数追踪)、tracepoint(静态追踪点)、uprobe(用户态函数追踪),以及 USDT 静态用户态追踪点,并通过实战代码展示每种 Hook 的使用方式与适用场景。