文章

eBPF 工具链实战

eBPF 工具链实战(应用型)

一、bpftool — 审计内核里到底挂了什么

bpftool 是内核自带的多功能 CLI,是看清”黑盒里挂了什么 eBPF”的标准工具。子命令:prog / map / link / net / perf / btf

prog — 已加载的程序

bpftool prog list
# 4: sched_cls  tag 3a1f...  gpl  attached to clsact:docker0
#   loaded_at 12:30 uid 0 xlated 512B jited 320B memlock 4096B

bpftool prog show id 4          # 详情:指令数、jit、map 关联
bpftool prog dump xlated id 4   # 反汇编字节码(人类可读)
bpftool prog dump jited id 4    # 反汇编机器码

字段解读:tag 是程序哈希(同程序同 tag,便于比对);xlated=字节码大小,jited=机器码大小;attached to 显示挂载点。

net — 网络挂载点

bpftool net
# xdp dev eth0
# tc  dev eth0 ingress
# tc  dev cilium_host egress

map — BPF Map

bpftool map list                       # 所有 map,看容量/条目数(排查 map 爆了)
bpftool map dump id <MAP_ID>           # 看内容(如 Cilium LB 映射)
bpftool map lookup id <ID> key <HEX>   # 查单键
bpftool link list        # 程序与 hook 的绑定关系(比 prog 更细)
bpftool perf list        # 正在采样的 perf_event 程序

临时摘钩验证(排障用)

bpftool net detach xdp dev eth0        # 临时卸载 XDP,验证"是不是 eBPF 干的"
bpftool net detach tc ingress dev eth0

怀疑 eBPF 引起异常时:先 prog list 确认有程序在跑 → net 看挂哪 → 必要时临时 detach 验证。

二、bpftrace — 一行式排障语言

bpftrace 是应用型排障神器:不用写 C、不用编译,直接 attach 到内核 hook 看现场。语法类 awk。

核心语法

元素含义例子
probe挂载点kprobe:vfs_readtracepoint:tcp:tcp_retransmit_skbprofile:hz:99softirq:*usdt:*
{}触发时动作{ @[comm]=count(); }
@nameBPF Map(自动聚合)@[pid]@us
filter前置条件/pid==12345//@start[tid]/
nsecs / ktime纳秒时间戳算延迟
args->xxxtracepoint 参数args->saddrargs->dev
comm / pid / tid内置变量
ustack / kstack用户/内核栈火焰图

Map 聚合函数

函数作用
count()计数
sum(x) / avg(x)求和 / 平均
min(x) / max(x)最小 / 最大
hist(x)幂次直方图(2 的幂)
lhist(x, min, max, step)线性直方图
delete(@x)删除键(避免泄漏)

实用脚本库(SRE 直接抄,多数需 root)

CPU

# 谁在吃 CPU(每 5s 打印)
bpftrace -e 'profile:hz:99 { @[comm]=count(); } interval:s:5 { print(@); clear(@); }'
# 某进程 on-CPU 火焰栈
bpftrace -e 'profile:hz:99 /pid==12345/ { @[ustack]=count(); }'
# 内核态热点
bpftrace -e 'profile:hz:99 { @[kstack]=count(); }'

IO / 存储

# 块设备 I/O 延迟直方图
bpftrace -e 'tracepoint:block:block_rq_issue { @s[args->dev,args->sector]=nsecs; }
tracepoint:block:block_rq_complete /@s[args->dev,args->sector]/ {
  @us=hist((nsecs-@s[args->dev,args->sector])/1000); delete(@s[args->dev,args->sector]); }'
# 谁在大量写盘
bpftrace -e 'tracepoint:block:block_rq_issue /args->rwbs[0]=='"'"'W'"'"'/ { @[comm]=count(); }'

网络

# TCP 重传计数(定位烂链路)
bpftrace -e 'tracepoint:tcp:tcp_retransmit_skb { @[args->saddr,args->daddr]=count(); }'
# 主动/被动连接建立数
bpftrace -e 'tracepoint:tcp:tcp_connect { @active[args->daddr]=count(); }
kprobe:tcp_v4_syn_recv_sock { @passive=count(); }'
# 某端口流量字节数
bpftrace -e 'kprobe:tcp_sendmsg /args->sk->__sk_common.skc_dport==htons(80)/ { @bytes=sum(args->size); }'

内存

# 缺页异常(可能内存压力)
bpftrace -e 'software:page-faults:1 { @[comm]=count(); }'
# 谁在 alloc 大块内存
bpftrace -e 'uretprobe:/lib/x86_64-linux-gnu/libc.so.6:malloc /retval>1048576/ { @[comm]=sum(retval); }'

调度 / 延迟

# 进程唤醒→运行 的调度延迟
bpftrace -e 'tracepoint:sched:sched_wakeup { @w[args->pid]=nsecs; }
tracepoint:sched:sched_switch /@w[args->next_pid]/ {
  @lat=hist((nsecs-@w[args->next_pid])/1000); delete(@w[args->next_pid]); }'
# 进程被抢占/阻塞时长(oncpu 时间)
bpftrace -e 'tracepoint:sched:sched_switch { @oncpu[args->prev_comm]=hist((nsecs-@last[args->prev_pid])/1000); @last[args->prev_pid]=nsecs; }'

进程 / 文件系统

# exec 泄漏 / fork bomb 检测
bpftrace -e 'tracepoint:syscalls:sys_enter_execve { @[comm]=count(); }'
# 某文件 open 延迟
bpftrace -e 'kprobe:vfs_open /str(args->filename)=="/var/log/app.log"/ { @s=nsecs; }
kretprobe:vfs_open /@s/ { @us=hist((nsecs-@s)/1000); delete(@s); }'
# 哪些文件被频繁读
bpftrace -e 'kprobe:vfs_read { @[str(args->file->f_path.dentry->d_name.name)]=count(); }'

锁 / 同步

# 谁持有了某个 mutex 久(futex 等待)
bpftrace -e 'tracepoint:futex:futex_wait_start { @s[tid]=nsecs; }
tracepoint:futex:futex_wake { @us=hist((nsecs-@s[tid])/1000); delete(@s[tid]); }'

容器 / cgroup

# 某 cgroup 内所有进程的 CPU 聚合(结合 [[../cgroup v2 详解]])
bpftrace -e 'profile:hz:99 { @[cgroup]=count(); }'
# fork 进程数(配合 pids 控制器限流)
bpftrace -e 'tracepoint:sched:sched_process_fork { @[args->parent_comm]=count(); }'

这些脚本本质就是”处理逻辑写在内核、只把聚合结果吐出来”——正是 eBPF 性能模型 那节讲的”每聚合一次”。

三、BCC — 现成工具集

BCC(BPF Compiler Collection)提供一堆开箱即用的 Python 工具,不用自己写:

工具功能
execsnoop实时显示新进程(排查异常启动)
biosnoop块设备 I/O 追踪(含延迟)
tcplife / tcpconnectTCP 连接生命周期 / 主动连接
opensnoop追踪 open() 系统调用
cachestat页缓存命中率
runqlatCPU 运行队列延迟
biolatency块设备 I/O 延迟直方图
softirqs / hardirqs软/硬中断耗时
execsnoop -x            # 只看失败 exec
biosnoop -Q             # 排队延迟
tcplife -p 12345        # 只看某 pid 的 TCP 连接

四、Cilium CLI

cilium status                      # 整体状态(含 eBPF 程序加载与否)
cilium status --verbose
cilium connectivity test           # 内置连通性测试
cilium endpoint list               # 所有 Endpoint 及策略状态
cilium bpf lb list                 # Service→Endpoint 的 eBPF 映射
cilium bpf ct list                 # conntrack 表
cilium hubble port-forward &       # 转发 Hubble UI
hubble observe --verdict DROPPED   # 看被丢的流量

五、libbpf(开发型预览)

libbpf 是 C 库,生产级 eBPF 程序的加载框架(CO-RE、BTF)。应用型了解即可——Cilium/BCC/bpftrace 底层都基于它。真正自己写生产 BPF 程序时再深入(第 2 档)。

关联知识

学习时间

阶段时间备注
工具链2026-07-16bpftool 五子命令、bpftrace 语法+12 类脚本、BCC 工具集、Cilium CLI