eBPF 在嵌入式 Linux 的应用

一、什么是 eBPF

二、能力概览

                  ┌─ 安全 ─→ 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. 网络优化

3. 安全审计

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. 概念

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 限制

  1. 需要内核支持
    CONFIG_BPF=y CONFIG_BPF_SYSCALL=y CONFIG_BPF_JIT=y CONFIG_HAVE_EBPF_JIT=y CONFIG_CGROUP_BPF=y

  2. 架构 JIT
    - ARM64:完整 JIT
    - ARMv7:部分 JIT(取决于 CPU)
    - RISC-V:完整支持
    - MIPS:partial

  3. 资源限制
    - 内核栈 512 字节
    - BPF 程序大小限制
    - Map 大小限制

  4. 调试
    - 打印受限(bpf_trace_printk)
    - 复杂逻辑难写
    - 用 bpftrace/BCC 简化

九、Libbpf / CO-RE

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(更轻)