eBPF 探针类型:kprobe、tracepoint、uprobe、fentry 与挂载点
eBPF 程序本身只是字节码,必须挂到探针或钩子上才有事件来源。常见类型:kprobe/kretprobe(内核函数)、tracepoint(内核静态埋点)、uprobe/uretprobe(用户态函数)、USDT(用户态静态探针)、fentry/fexit(基于 BTF 的函数入口/返回,5.5+)、perf_event(采样),以及 XDP/tc/cgroup/LSM 等数据面钩子。
/sys/kernel/btf/vmlinux 存在才方便使用 CO-RE、fentry 和结构化读取内核类型。CAP_BPF + CAP_PERFMON(5.8+ 能力拆分);kernel.unprivileged_bpf_disabled 通常禁止非特权 BPF。bpf() 的 seccomp、可用的 tracefs/BTF,且程序作用于宿主内核;K8s 中通常是特权 DaemonSet。# 确认内核 BTF 与 eBPF 能力
ls -l /sys/kernel/btf/vmlinux
sudo bpftool feature probe | head -30
# 列出可用的 kprobe 与 tracepoint
sudo bpftrace -l 'kprobe:vfs_*' | head
sudo bpftrace -l 'tracepoint:syscalls:sys_enter_*' | head
# 用 kprobe 统计 vfs_read 调用次数(按进程聚合,避免逐条打印)
sudo bpftrace -e 'kprobe:vfs_read { @[comm] = count(); }'
# 用 tracepoint 读取 openat 的文件名,稳定性和可移植性更好
sudo bpftrace -e 'tracepoint:syscalls:sys_enter_openat { printf("%s %s\n", comm, str(args->filename)); }'
# fentry 需要 5.5+ 与 BTF,开销通常低于 kprobe
sudo bpftrace -e 'fentry:vfs_read { @reads[comm] = count(); }'
选探针时优先 tracepoint;没有合适 tracepoint 再用 fentry/fexit;最后才用 kprobe,并尽量在 map 中聚合。
NOKPROBE_SYMBOL/notrace 路径;同时 kprobe 函数名会随内核版本变化,换 tracepoint 或改挂调用者更稳。CONFIG_DEBUG_INFO_BTF,或 /sys/kernel/btf/vmlinux 不存在;老内核只能用 kprobe。str()、bpf_probe_read_kernel()/bpf_probe_read_user()(老内核只有 bpf_probe_read()),否则可能读到错误地址或被拒绝。printf 会淹没系统;先在 map 里计数/直方图聚合,或只对目标 PID/CPU 采样。bpf()、缺少 CAP_BPF、/sys 只读、tracefs 未挂载;先看 bpftool feature probe 和 capsh --print。dmesg | grep -i lockdown 可确认。bpf()、缺少 CAP_BPF/CAP_PERFMON、/sys 只读或没有挂载 tracefs,且程序作用于宿主内核。