eBPF 在嵌入式 Linux 的应用
一、什么是 eBPF
- 起源:Berkeley Packet Filter(1992)
- 现代 eBPF(extended BPF):Linux 3.18+
- 通用内核沙箱技术
- 应用:网络、安全、追踪、性能分析
二、能力概览
┌─ 安全 ─→ KRSI(LSM)
│
├─ 网络 ─→ XDP、tc、socket
│
内核 eBPF 程序 ─→ ├─ 追踪 ─→ kprobe、tracepoint
│
├─ 性能 ─→ perf、profile
│
└─ 监控 ─→ systemtap
三、eBPF 程序类型
| 类型 | 用途 |
|---|---|
BPF_PROG_TYPE_KPROBE |
内核函数追踪 |
BPF_PROG_TYPE_TRACEPOINT |
Tracepoint |
BPF_PROG_TYPE_PERF_EVENT |
perf 事件 |
BPF_PROG_TYPE_SOCKET_FILTER |
socket 过滤 |
BPF_PROG_TYPE_XDP |
网络最早点 |
BPF_PROG_TYPE_SCHED_CLS |
流量控制 |
BPF_PROG_TYPE_LSM |
安全钩子 |
BPF_PROG_TYPE_STRUCT_OPS |
自定义结构 |
BPF_PROG_TYPE_SK_LOOKUP |
socket 查找 |
BPF_PROG_TYPE_SYSCALL |
系统调用 |
四、嵌入式使用场景
1. 实时性能监控
- 不需要重启
- 低开销
- 可动态加载 / 卸载
2. 网络优化
- XDP 在 NPU 之前处理包
- 极快转发(数千万 PPS)
3. 安全审计
- LSM 钩子
- 系统调用白名单
- 进程权限控制
4. 故障排查
- 现场抓数据
- 不需重编内核
五、bpftrace 入门
1. 安装
apt install bpftrace
bpftrace --version
2. 单行命令
# 系统调用计数
bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @[comm] = count(); }'
# open 文件名
bpftrace -e 'kprobe:do_sys_open { printf("%s\n", argstr(1)); }'
# 网络 connect
bpftrace -e '
tracepoint:syscalls:sys_enter_connect {
printf("%s → connect\n", comm);
}
'
# CPU 火焰图采样
bpftrace -e '
profile:hz:99 {
@[kstack] = count();
}
'
3. 内置变量
# pid, tid, comm(进程名)
# nsecs(时间戳)
# args(tracepoint 参数)
# arg0..arg5(kprobe 参数)
# retval(kretprobe 返回值)
# kstack(内核栈)
# ustack(用户栈)
# ktime(开机时间)
4. 内置函数
# count, sum, avg, hist, lhist, min, max
# print, printf
# kstack, ustack, ksym, usym
# str(args)
# ntop, join
# exit() 退出
5. 数据输出
bpftrace -e '
tracepoint:block:block_rq_issue {
@bytes = hist(args->bytes);
}
'
Ctrl-C
@bytes:
[1] 0 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@|
[2, 4] 0 | |
[4, 8] 5 |@@@@ |
[8, 16] 20 |@@@@@@@@@@@@@@@ |
[16, 32] 40 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ |
[32, 64] 10 |@@@@@@ |
[64, 128] 3 |@@ |
六、BCC 工具集
1. 安装
apt install bpfcc-tools
ls /usr/sbin/*-bpfcc /usr/share/bcc/tools/
2. 常用工具
opensnoop-bpfcc # open 系统调用
execsnoop-bpfcc # exec
tcpconnect-bpfcc # TCP connect
tcpaccept-bpfcc # TCP accept
biolatency-bpfcc # 块 IO 延迟
runqlat-bpfcc # 运行队列延迟
offcputime-bpfcc # 阻塞时间
profile-bpfcc # 火焰图
softirqs-bpfcc
hardirqs-bpfcc
3. 示例:分析应用启动慢
# 看磁盘 IO 阻塞
biolatency-bpfcc -d /dev/sda 30
# 看 CPU 阻塞
runqlat-bpfcc 30 1
# 看系统调用
execsnoop-bpfcc
七、XDP(eXpress Data Path)
1. 概念
- 包进入网络栈最早点
- 不经过协议栈
- 极快(数千万 PPS)
- 用途:防火墙、DDoS、LB、加速
2. 示例:丢包
SEC("xdp")
int drop_packet(struct xdp_md *ctx)
{
return XDP_DROP;
}
3. 示例:改包
SEC("xdp")
int rewrite_mac(struct xdp_md *ctx)
{
void *data = (void *)(long)ctx->data;
void *data_end = (void *)(long)ctx->data_end;
struct ethhdr *eth = data;
if ((void*)eth + 1 > data_end) return XDP_PASS;
// 改 MAC 地址
memcpy(eth->h_dest, "\xff\xff\xff\xff\xff\xff", 6);
return XDP_TX;
}
4. 加载
ip link set dev eth0 xdp obj my_xdp.o
ip link set dev eth0 xdp off
八、嵌入式 eBPF 限制
-
需要内核支持
CONFIG_BPF=y CONFIG_BPF_SYSCALL=y CONFIG_BPF_JIT=y CONFIG_HAVE_EBPF_JIT=y CONFIG_CGROUP_BPF=y -
架构 JIT
- ARM64:完整 JIT
- ARMv7:部分 JIT(取决于 CPU)
- RISC-V:完整支持
- MIPS:partial -
资源限制
- 内核栈 512 字节
- BPF 程序大小限制
- Map 大小限制 -
调试
- 打印受限(bpf_trace_printk)
- 复杂逻辑难写
- 用 bpftrace/BCC 简化
九、Libbpf / CO-RE
- Libbpf:标准 BPF 用户态库
- CO-RE(Compile Once, Run Everywhere)
- BTF(BPF Type Format)
- 一次编译,多内核运行
apt install libbpf-dev
// CO-RE 风格
SEC("tp/sched/sched_process_exec")
int handle_exec(struct trace_event_raw_sched_process_exec *ctx)
{
u32 pid = bpf_get_current_pid_tgid() >> 32;
bpf_printk("exec: pid=%d", pid);
return 0;
}
十、典型嵌入式 eBPF 项目
| 项目 | 用途 |
|---|---|
| Cilium | 容器网络(Kubernetes) |
| Falco | 运行时安全 |
| Tracee | 行为追踪 |
| Pixie | 应用调试(New Relic) |
| Hubble | 可观测性 |
| bpftool | 内核 / 用户态通用工具 |
十一、bpftool
apt install linux-tools-common
bpftool prog show # 已加载的程序
bpftool map show
bpftool prog dump xlated id 1 # 看字节码
bpftool prog dump jited id 1 # 看 JIT
bpftool trace show
十二、嵌入式选择建议
| 场景 | 方案 |
|---|---|
| 简单追踪 | bpftrace |
| 复杂追踪 | BCC |
| 高性能包处理 | XDP |
| 长期监控 | libbpf + 自定义 |
| 快速 debug | ftrace(更轻) |