eBPF 从包过滤器进化成内核的通用可编程框架——networking、tracing、security、scheduling 全覆盖。
一、先看图
flowchart TD
SRC[BPF C 源码] --> CLANG[clang -target bpf]
CLANG --> OBJ[BPF 字节码<br/>.o ELF]
OBJ --> LOAD[bpf syscall<br/>BPF_PROG_LOAD]
LOAD --> VERIFY[verifier<br/>安全检查]
VERIFY --> JIT[JIT 编译<br/>→ 本机码]
JIT --> ATTACH[attach 到 hook<br/>kprobe/tracepoint/XDP...]
ATTACH --> MAP[BPF maps<br/>用户态 ↔ 内核态通信]
classDef build fill:#388bfd22,stroke:#388bfd,color:#adbac7;
classDef kern fill:#3fb95022,stroke:#3fb950,color:#adbac7;
class SRC,CLANG,OBJ build
class LOAD,VERIFY,JIT,ATTACH,MAP kern
二、程序类型
| 类型 | 挂载点 | 用途 |
|---|---|---|
| kprobe | 内核函数 | 追踪 |
| tracepoint | 静态探针 | 追踪 |
| XDP | 网卡入口 | 高速包处理 |
| tc | 流量控制 | 网络策略 |
| cgroup | cgroup 钩子 | 资源控制 |
| LSM | 安全钩子 | 访问控制 |
| sched_ext | 调度器 | 自定义调度 |
| struct_ops | 内核回调结构 | 替换内核函数指针 |
三、verifier
bpf(BPF_PROG_LOAD, ...) → verifier 模拟执行所有路径
verifier 检查:
- 无越界内存访问
- 无无限循环(有界循环 6.x 允许)
- 所有路径都返回
- 辅助函数参数类型正确
- 不泄露内核指针到用户态
四、JIT
verifier 通过 → JIT 编译为本机码(x86、arm64、riscv)。
sysctl net.core.bpf_jit_enable=1 # 默认开启
sysctl net.core.bpf_jit_harden=2 # 生产加固JIT 后性能接近手写内核代码。
五、BPF Maps
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, u32);
__type(value, u64);
__uint(max_entries, 10240);
} my_map SEC(".maps");常用 map 类型:
| 类型 | 用途 |
|---|---|
| HASH | 通用键值 |
| ARRAY | 索引数组 |
| PERCPU_HASH/ARRAY | per-CPU 避免竞争 |
| RINGBUF | 事件输出到用户态 |
| LPM_TRIE | 前缀匹配(路由) |
| BLOOM_FILTER | 概率判断 |
六、BTF 与 CO-RE
6.1 BTF(BPF Type Format)
内核编译时生成类型信息 → BPF 程序可以按名称访问结构体字段。
6.2 CO-RE(Compile Once, Run Everywhere)
// libbpf CO-RE
u32 pid = BPF_CORE_READ(task, tgid);编译一次 → 运行在不同内核版本 → libbpf 根据 BTF 重定位字段偏移。
七、BPF_LSM
SEC("lsm/file_open")
int BPF_PROG(restrict_open, struct file *file)
{
// 自定义安全策略
return -EPERM;
}用 BPF 实现 LSM 钩子 → 动态安全策略 → 不需要编译内核模块。
八、sched_ext
SEC("struct_ops/enqueue")
void BPF_PROG(my_enqueue, struct task_struct *p, u64 enq_flags)
{
scx_bpf_dispatch(p, SCX_DSQ_GLOBAL, SCX_SLICE_DFL, enq_flags);
}用 BPF 写调度器 → 实验性调度策略 → 无需重编内核。
九、观察
bpftool prog list # 已加载 BPF 程序
bpftool map list # 已创建 map
bpftool prog dump xlated id 1 # 查看字节码
bpftool btf dump file /sys/kernel/btf/vmlinux | head
# bpftrace 快速追踪
bpftrace -e 'kprobe:vfs_read { @[comm] = count(); }'十、小结
- eBPF = 内核通用可编程框架
- verifier 保证安全 → JIT 保证性能
- maps 实现用户态 ↔︎ 内核态通信
- BTF + CO-RE 解决跨版本兼容
- BPF_LSM + sched_ext 扩展到安全和调度
参考文献
kernel/bpf/Documentation/bpf/- Brendan Gregg, “BPF Performance Tools.” 2019
- ebpf.io
工具
bpftoolbpftracelibbpfbcc
延伸阅读
上一篇:perf 子系统 下一篇:kprobe/uprobe
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【操作系统百科】内存回收
Linux 内存回收是 VM 最复杂的子系统之一。本文讲 active/inactive LRU、kswapd 与 direct reclaim、watermark 三线、swappiness 的真实含义、MGLRU 改造、memcg 回收与 PSI。
【操作系统百科】交换
swap 还值得开吗?本文讲 swap area 基础、swap cache、zram 压缩内存、zswap 前端压缩池、swappiness 的真实含义、容器里的 swap 策略,以及为什么现代 Android 全靠 zram 不靠磁盘。
【操作系统百科】Slab/SLUB 分配器
buddy 只管页粒度(4K+),内核大多数对象只有几十到几百字节。slab/SLUB 在 buddy 之上做对象级缓存。本文讲 slab 历史、SLUB 接手、SLOB 退场、kmem_cache、per-CPU cache、KASAN 集成。
【操作系统百科】用户态分配器
glibc malloc、tcmalloc、jemalloc、mimalloc 各有哲学。本文讲 arena、thread cache、size class、madvise 返还策略、碎片与 RSS 膨胀、如何根据负载选分配器。