跳转至

02-eBPF 与系统调用追踪

重要性: ⭐⭐⭐⭐ 实用度: ⭐⭐⭐⭐⭐ 学习时间: 2 天 必须掌握: 推荐了解


为什么学这一章

eBPF ( Extended Berkeley Packet Filter )是 Linux 内核的一项革命性技术,它允许你在内核中安全地运行自定义代码。学习 eBPF 能让你: - 实现高性能的系统监控和追踪 - 理解现代可观测性工具的底层原理 - 编写自定义的系统调用拦截器 - 掌握 Linux 内核编程的现代方法

学完这一章,你将能够: - ✅ 理解 eBPF 的工作原理和架构 - ✅ 使用 bpftrace 进行系统调用追踪 - ✅ 编写简单的 eBPF 程序 - ✅ 了解 eBPF 在云原生和可观测性领域的应用


📖 核心概念

1. 什么是 eBPF

Text Only
┌─────────────────────────────────────────────────────────────────────┐
│                    eBPF架构概览                                        │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  eBPF(Extended Berkeley Packet Filter)                              │
│  ├── 起源:1992年BSD包过滤器(BPF)                                   │
│  ├── 扩展:2014年Linux 3.18引入eBPF                                  │
│  └── 现状:Linux内核最重要的子系统之一                                │
│                                                                     │
│  核心特点:                                                           │
│  ┌───────────────────────────────────────────────────────────────┐ │
│  │                                                                │ │
│  │  1. 安全执行                                                     │ │
│  │     • 通过内核验证器(Verifier)检查程序安全性                   │ │
│  │     • 禁止无限循环、空指针解引用等危险操作                       │ │
│  │     • 保证程序不会崩溃内核                                       │ │
│  │                                                                │ │
│  │  2. 高性能                                                       │ │
│  │     • JIT编译:eBPF字节码编译为机器码                            │ │
│  │     • 内核态执行:无需用户态/内核态切换                          │ │
│  │     • 比传统系统调用追踪快10-100倍                               │ │
│  │                                                                │ │
│  │  3. 灵活性                                                       │ │
│  │     • 动态加载:无需重启内核或修改源码                           │ │
│  │     • 事件驱动:响应各种内核事件                                 │ │
│  │     • 可扩展:支持自定义数据结构(Map)                          │ │
│  │                                                                │ │
│  └───────────────────────────────────────────────────────────────┘ │
│                                                                     │
│  eBPF程序生命周期:                                                   │
│  ┌──────────┐    ┌──────────┐    ┌──────────┐    ┌──────────┐      │
│  │ 编写程序  │───→│ 编译成   │───→│ 内核验证  │───→│ JIT编译  │      │
│  │ (C/Rust) │    │ BPF字节码│    │ 安全检查  │    │ 机器码   │      │
│  └──────────┘    └──────────┘    └──────────┘    └──────────┘      │
│                                                      │              │
│                                                      ↓              │
│                                              ┌──────────────┐      │
│                                              │  附加到内核   │      │
│                                              │  钩子点执行   │      │
│                                              └──────────────┘      │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

2. eBPF 的应用场景

Text Only
┌─────────────────────────────────────────────────────────────────────┐
│                    eBPF应用场景                                        │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  1. 可观测性(Observability)                                         │
│  ┌───────────────────────────────────────────────────────────────┐ │
│  │                                                                │ │
│  │  • 系统调用追踪:strace的现代化替代                              │ │
│  │  │   bpftrace -e 'tracepoint:syscalls:sys_enter_openat         │ │
│  │  │               { printf("%s opened %s\n", comm, str(args->filename)); }' │ │
│  │                                                                │ │
│  │  • 性能分析:CPU、内存、I/O分析                                  │ │
│  │  │   bpftrace -e 'kprobe:do_nanosleep                           │ │
│  │  │               { @start[tid] = nsecs; }                       │ │
│  │  │               kretprobe:do_nanosleep                         │ │
│  │  │               /@start[tid]/                                  │ │
│  │  │               { @latency = hist(nsecs - @start[tid]); }'    │ │
│  │                                                                │ │
│  │  • 网络监控:TCP连接、丢包分析                                   │ │
│  │  │   bpftrace -e 'kprobe:tcp_drop                               │ │
│  │  │               { printf("TCP drop: %s\n", kstack()); }'       │ │
│  │                                                                │ │
│  └───────────────────────────────────────────────────────────────┘ │
│                                                                     │
│  2. 网络安全                                                          │
│  ┌───────────────────────────────────────────────────────────────┐ │
│  │                                                                │ │
│  │  • 包过滤:XDP(eXpress Data Path)高速包处理                    │ │
│  │  │   - 在网卡驱动层处理包,绕过内核网络栈                        │ │
│  │  │   - 可达线速(10Gbps+)                                       │ │
│  │                                                                │ │
│  │  • 防火墙:Cilium基于eBPF的容器网络安全                          │ │
│  │  │   - 替代iptables,性能提升10倍+                               │ │
│  │  │   - 支持L3-L7层策略                                           │ │
│  │                                                                │ │
│  │  • DDoS防护:Cloudflare使用eBPF进行流量清洗                      │ │
│  │                                                                │ │
│  └───────────────────────────────────────────────────────────────┘ │
│                                                                     │
│  3. 性能优化                                                          │
│  ┌───────────────────────────────────────────────────────────────┐ │
│  │                                                                │ │
│  │  • TCP加速:Google的TCP BBR拥塞控制算法                          │ │
│  │  │   - 使用eBPF在内核中实现                                      │ │
│  │                                                                │ │
│  │  • 负载均衡:Facebook的Katran L4负载均衡器                       │ │
│  │  │   - 替换IPVS,性能提升10倍                                    │ │
│  │                                                                │ │
│  │  • 文件系统加速:eBPF加速ext4/xfs                                │ │
│  │                                                                │ │
│  └───────────────────────────────────────────────────────────────┘ │
│                                                                     │
│  4. 云原生                                                            │
│  ┌───────────────────────────────────────────────────────────────┐ │
│  │                                                                │ │
│  │  • 容器安全:Falco运行时安全检测                                 │ │
│  │  │   - 检测容器中的异常行为                                      │ │
│  │  │   - 监控文件系统、网络、进程活动                              │ │
│  │                                                                │ │
│  │  • 服务网格:Cilium替代Envoy实现服务网格                         │ │
│  │  │   - 数据平面使用eBPF                                          │ │
│  │  │   - 延迟降低50%+                                              │ │
│  │                                                                │ │
│  │  • 无服务器:AWS Lambda使用eBPF进行监控                          │ │
│  │                                                                │ │
│  └───────────────────────────────────────────────────────────────┘ │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

3. bpftrace 快速入门

bpftrace 是基于 eBPF 的高级追踪语言,类似 awk 的语法,非常适合快速编写追踪脚本。

安装 bpftrace

Bash
# Ubuntu/Debian
sudo apt-get install bpftrace

# CentOS/RHEL
sudo yum install bpftrace

# macOS(使用Docker或VM)
brew install bpftrace

基础语法

Text Only
┌─────────────────────────────────────────────────────────────────────┐
│                    bpftrace语法结构                                    │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  bpftrace程序结构:                                                    │
│                                                                     │
│  探针(Probe) + 过滤条件(Predicate) + 动作(Action)               │
│                                                                     │
│  示例:                                                               │
│  ```bpftrace                                                        │
│  probe /filter/ { action }                                          │
│  ```                                                                │
│                                                                     │
│  探针类型:                                                           │
│  ┌───────────────────────────────────────────────────────────────┐ │
│  │  类型              │  示例                    │  说明          │ │
│  ├───────────────────────────────────────────────────────────────┤ │
│  │  kprobe            │  kprobe:do_sys_open      │  内核函数入口  │ │
│  │  kretprobe         │  kretprobe:do_sys_open   │  内核函数返回  │ │
│  │  tracepoint        │  tracepoint:syscalls:sys_enter_openat  │  内核跟踪点    │ │
│  │  uprobe            │  uprobe:/bin/bash:readline            │  用户函数入口  │ │
│  │  usdt              │  usdt:/usr/bin/python:function__entry │  用户静态跟踪点│ │
│  │  profile           │  profile:hz:99           │  定时采样      │ │
│  │  interval          │  interval:s:1            │  定时触发      │ │
│  │  software          │  software:page-faults    │  内核软件事件  │ │
│  │  hardware          │  hardware:cache-misses   │  硬件性能事件  │ │
│  └───────────────────────────────────────────────────────────────┘ │
│                                                                     │
│  内置变量:                                                           │
│  ┌───────────────────────────────────────────────────────────────┐ │
│  │  变量      │  说明                                              │ │
│  ├───────────────────────────────────────────────────────────────┤ │
│  │  pid       │  进程ID                                            │ │
│  │  tid       │  线程ID                                            │ │
│  │  comm      │  进程名                                            │ │
│  │  nsecs     │  当前时间(纳秒)                                  │ │
│  │  cpu       │  CPU编号                                           │ │
│  │  uid       │  用户ID                                            │ │
│  │  gid       │  组ID                                              │ │
│  │  args      │  函数参数(需要类型转换)                          │ │
│  │  retval    │  返回值(kretprobe)                               │ │
│  │  kstack    │  内核调用栈                                        │ │
│  │  ustack    │  用户调用栈                                        │ │
│  │  arg0-arg9 │  函数参数(整数)                                  │ │
│  └───────────────────────────────────────────────────────────────┘ │
│                                                                     │
│  内置函数:                                                           │
│  ┌───────────────────────────────────────────────────────────────┐ │
│  │  函数              │  说明                                      │ │
│  ├───────────────────────────────────────────────────────────────┤ │
│  │  printf(fmt, ...)  │  格式化输出                                │ │
│  │  str(char*)        │  将char*转换为字符串                       │ │
│  │  join(char*[])     │  将字符串数组用空格连接                    │ │
│  │  hist(int)         │  生成直方图(2的幂次分桶)                 │ │
│  │  lhist(int,...)    │  生成线性直方图                            │ │
│  │  count()           │  计数                                      │ │
│  │  sum(int)          │  求和                                      │ │
│  │  avg(int)          │  平均值                                    │ │
│  │  min(int)          │  最小值                                    │ │
│  │  max(int)          │  最大值                                    │ │
│  │  delete(@x)        │  删除map中的键                             │ │
│  │  clear(@x)         │  清空map                                   │ │
│  │  exit()            │  退出程序                                  │ │
│  │  time(fmt)         │  格式化时间                                │ │
│  └───────────────────────────────────────────────────────────────┘ │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

🧪 动手实验

实验 1 :系统调用追踪

目的:使用 bpftrace 追踪系统调用

步骤

  1. 追踪 openat 系统调用
Bash
# 追踪所有进程的文件打开操作
sudo bpftrace -e 'tracepoint:syscalls:sys_enter_openat {
    printf("%s (PID %d) opened: %s\n", comm, pid, str(args->filename));
}'

# 输出示例:
# bash (PID 1234) opened: /etc/passwd
# ls (PID 5678) opened: /home/user/file.txt
  1. 统计系统调用频率
Bash
# 统计每个进程的系统调用次数
sudo bpftrace -e 'tracepoint:raw_syscalls:sys_enter {
    @[comm] = count();
}'

# 按Ctrl+C后输出:
# @[bash]: 1234
# @[chrome]: 56789
# @[python]: 345
  1. 追踪进程创建
Bash
# 追踪fork和exec
sudo bpftrace -e 'tracepoint:syscalls:sys_enter_execve {
    printf("%s (PID %d) executing: %s\n",
           comm, pid, str(args->filename));
}'

实验 2 :性能分析

目的:分析程序性能瓶颈

步骤

  1. CPU 采样分析
Bash
# 每秒采样99次(避免与定时器中断冲突)
sudo bpftrace -e 'profile:hz:99 {
    @[kstack] = count();
}'

# 输出内核热点函数调用栈
  1. 函数延迟直方图
Bash
# 测量read系统调用的延迟分布
sudo bpftrace -e 'kprobe:ksys_read {
    @start[tid] = nsecs;
}
kretprobe:ksys_read /@start[tid]/ {
    @latency = hist(nsecs - @start[tid]);
    delete(@start[tid]);
}'

# 输出:
# @latency:
# [0, 1]          1234 |@@@@@@@@@@@@@@@@@@@@|
# [2, 4)           567 |@@@@@@@@@|
# [4, 8)           234 |@@@@|
  1. 文件系统 I/O 分析
Bash
# 追踪VFS读写操作
sudo bpftrace -e 'kprobe:vfs_read, kprobe:vfs_write {
    @[func, comm] = sum(arg2);  // 统计读写字节数
}'

实验 3 :网络追踪

目的:分析网络行为

步骤

  1. 追踪 TCP 连接
Bash
# 追踪TCP连接建立
sudo bpftrace -e 'kprobe:tcp_v4_connect {
    printf("%s (PID %d) connecting to: ", comm, pid);
}
kprobe:inet_csk_complete_hashdance {
    printf("Connection established\n");
}'
  1. 丢包分析
Bash
# 追踪TCP丢包
sudo bpftrace -e 'kprobe:tcp_drop {
    printf("TCP packet dropped by %s\n", kstack());
}'
  1. 网络延迟
Bash
# 测量网络RTT
sudo bpftrace -e 'kprobe:tcp_sendmsg {
    @start[tid] = nsecs;
}
kprobe:tcp_recvmsg /@start[tid]/ {
    @rtt = hist(nsecs - @start[tid]);
    delete(@start[tid]);
}'

实验 4 :编写 eBPF C 程序

目的:使用 BCC 编写自定义 eBPF 程序

步骤

  1. 安装 BCC
Bash
# Ubuntu
sudo apt-get install bpfcc-tools linux-headers-$(uname -r)  # $()命令替换:执行命令并获取输出

# Python库
pip install bcc
  1. 编写 eBPF 程序
Python
#!/usr/bin/env python3
# hello_ebpf.py

from bcc import BPF

# eBPF C程序
program = """
#include <linux/sched.h>

// 定义数据结构
struct data_t {
    u32 pid;
    u32 uid;
    char comm[TASK_COMM_LEN];
    char message[12];
};

// 定义perf输出缓冲区
BPF_PERF_OUTPUT(events);

int hello(void *ctx) {
    struct data_t data = {};

    // 获取当前进程信息
    data.pid = bpf_get_current_pid_tgid() >> 32;
    data.uid = bpf_get_current_uid_gid() & 0xFFFFFFFF;
    bpf_get_current_comm(&data.comm, sizeof(data.comm));

    // 设置消息
    bpf_probe_read_kernel_str(&data.message, sizeof(data.message), "Hello eBPF!");

    // 提交事件到用户态
    events.perf_submit(ctx, &data, sizeof(data));

    return 0;
}
"""

# 加载eBPF程序
b = BPF(text=program)

# 附加到系统调用
b.attach_kprobe(event=b.get_syscall_fnname("clone"), fn_name="hello")

# 定义回调函数
def print_event(cpu, data, size):
    event = b["events"].event(data)
    print(f"PID: {event.pid}, UID: {event.uid}, Comm: {event.comm.decode()}, Message: {event.message.decode()}")

# 循环读取事件
print("Tracing... Press Ctrl+C to exit")
b["events"].open_perf_buffer(print_event)
while True:
    try:  # try/except捕获异常
        b.perf_buffer_poll()
    except KeyboardInterrupt:
        break
  1. 运行程序
Bash
sudo python3 hello_ebpf.py

# 在另一个终端执行:
ls

# 输出:
# Tracing... Press Ctrl+C to exit
# PID: 1234, UID: 1000, Comm: bash, Message: Hello eBPF!

💡 核心要点总结

eBPF vs 传统工具

特性 strace perf eBPF/bpftrace
性能开销 高(每次系统调用都中断) 低( JIT 编译,内核态执行)
灵活性 低(固定功能) 高(可编程)
实时性 实时 采样 实时
学习曲线 中高
生产环境 不推荐 可用 推荐

eBPF 最佳实践

  1. 性能优先
  2. 使用 tracepoint 而非 kprobe (更稳定)
  3. 避免在内核中做复杂计算
  4. 使用 per-CPU map 减少锁竞争

  5. 安全考虑

  6. 始终检查 eBPF 程序的返回值
  7. 避免在生产环境直接运行未测试的程序
  8. 注意隐私合规(不要追踪敏感数据)

  9. 调试技巧

  10. 使用bpftrace -d查看调试输出
  11. 使用/sys/kernel/debug/tracing/trace_pipe查看内核日志
  12. 使用bpftool prog list查看加载的程序

常用工具链

Text Only
┌─────────────────────────────────────────────────────────────────────┐
│                    eBPF工具链                                          │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  高级工具(易用)                                                      │
│  ├── bpftrace:类awk的追踪语言,适合快速脚本                          │
│  ├── bcc:Python/C++库,适合复杂程序                                  │
│  └── kubectl-trace:Kubernetes中的bpftrace                            │
│                                                                     │
│  开发工具                                                            │
│  ├── libbpf:C/C++ eBPF加载库                                         │
│  ├── libbpf-bootstrap:eBPF程序模板                                   │
│  ├── bpftool:eBPF程序管理工具                                        │
│  └── LLVM/Clang:编译eBPF程序                                         │
│                                                                     │
│  可视化平台                                                          │
│  ├── Grafana + Prometheus + eBPF exporter                            │
│  ├── Pixie:Kubernetes可观测性平台                                    │
│  └── Groundcover:eBPF应用性能监控                                    │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

❓ 常见问题

Q1 : eBPF 和内核模块有什么区别?

A :主要区别: - 安全性: eBPF 有验证器保证安全,内核模块可能崩溃系统 - 易用性: eBPF 无需编译内核,可动态加载卸载 - 性能: eBPF 有 JIT 编译,性能接近原生代码 - 灵活性:内核模块功能更强大,但风险更高

Q2 : eBPF 对内核版本有什么要求?

A : - 基础功能: Linux 4.1+ - BPF Map: Linux 4.3+ - BPF 程序类型扩展: Linux 4.9+ - BPF trampoline: Linux 5.5+ - 推荐:使用 Linux 5.10+( LTS 版本)

Q3 : eBPF 程序会影响系统性能吗?

A : - 设计目标: eBPF 追求最小性能开销 - 典型开销:<1% CPU (简单追踪) - 注意事项: - 避免高频事件(如每个包处理) - 减少 map 查找次数 - 使用 per-CPU 数据结构

Q4 :如何学习 eBPF 编程?

A :学习路径: 1. 学习 bpftrace ( 1-2 天) 2. 学习 BCC Python 编程( 1 周) 3. 学习 libbpf C 编程( 2 周) 4. 阅读开源项目( Cilium 、 Falco 等) 5. 实践:编写自己的工具


📚 扩展阅读

  1. 《 Linux Observability with BPF 》 - David Calavera
  2. BPF 性能工具: brendangregg.com/bpf.html
  3. bpftrace 参考指南: GitHub.com/iovisor/bpftrace/blob/master/docs/reference_guide.md
  4. eBPF.io: ebpf.io (社区网站)
  5. Cilium 文档: docs.cilium.io

🎯 下一步

继续学习操作系统交互的后续内容,深入了解进程调度、内存管理、文件系统等核心机制。