2. eBPF 基础:虚拟机架构、程序类型与 Map 数据结构

eBPF(extended Berkeley Packet Filter)是 Linux 内核中一种革命性的沙箱化运行时环境。它允许用户在无需修改内核源码或加载内核模块的情况下,安全、高效地扩展内核功能。在 Cilium 中,eBPF 是实现网络策略、负载均衡、可观测性以及缓存命中等关键能力的核心技术基石。理解 eBPF 的底层架构是进行故障排查的前提。

2.1 eBPF 虚拟机架构

eBPF 并非直接运行用户编写的 C 代码,而是通过一个轻量级的、运行在内核空间的虚拟指令集(eBPF ISA)来执行。其架构设计确保了安全性与可移植性。

核心组件: eBPF 虚拟机由 11 个 64 位寄存器(R0-R10)、一个程序计数器(PC)、一个 512 字节的栈空间以及一个辅助函数(Helper Function)调用接口组成。
eBPF 虚拟机架构与执行流程 用户空间 (Userspace) eBPF 验证器 (Verifier) 静态分析 · 循环限制 · 指针检查 JIT 编译器 (Just-In-Time) 将 eBPF 字节码转为原生机器码 内核空间 (Kernel Space) eBPF 程序 (Program) 11 个寄存器 · 512B 栈 辅助函数调用 eBPF Map (数据结构) 哈希表 · 数组 · LRU · Per-CPU 用户态与内核态共享数据 事件钩子:XDP · TC · Kprobe · Tracepoint · cgroup

2.2 eBPF 程序类型

eBPF 程序通过挂载到内核的不同事件钩子(Hook)来触发执行。不同的程序类型决定了其上下文、能力以及触发时机。在 Cilium 网络场景中,最核心的三种类型是 XDP、TC 和 Kprobe。

程序类型 挂载点 触发时机 Cilium 中的典型用途
XDP 网卡驱动层(最早) 接收数据包时,在 SKB 分配前 DDoS 防护、负载均衡(Maglev)、早期丢包
TC 内核网络协议栈(ingress/egress) 数据包进入/离开协议栈时 网络策略执行、封装/解封装(VXLAN/Geneve)、带宽管理
Kprobe 内核函数入口/出口 内核函数被调用时 动态追踪、性能分析、调试内核函数调用链
故障排查提示: 当遇到 Cilium 缓存未命中时,首先应确认 eBPF 程序是否正确挂载。使用 bpftool prog list 可以查看当前系统中所有加载的 eBPF 程序及其类型。例如,查找类型为 xdp 或 sched_cls(TC)的程序。

2.3 eBPF Map 数据结构

eBPF Map 是 eBPF 程序与用户空间程序之间,以及不同 eBPF 程序之间进行数据交换的核心机制。它是一种通用的键值对存储结构,支持多种类型。Cilium 大量使用 Map 来存储连接跟踪表、策略规则、端点信息以及缓存条目。

2.3.1 常用 Map 类型对比

Map 类型 数据结构 特点 Cilium 应用场景
BPF_MAP_TYPE_HASH 哈希表 通用键值查找,支持动态增删 策略规则表、端点映射表
BPF_MAP_TYPE_ARRAY 数组 索引访问,预分配固定大小,查找速度快 全局配置、统计计数器
BPF_MAP_TYPE_LRU_HASH LRU 哈希表 自动淘汰最近最少使用的条目 连接跟踪表(CT Map)、缓存
BPF_MAP_TYPE_PERCPU_HASH Per-CPU 哈希表 每个 CPU 核心一份副本,无锁并发 高并发统计、性能敏感路径

2.3.2 Map 操作与缓存命中

在 Cilium 的数据路径中,eBPF 程序通过 bpf_map_lookup_elem() 辅助函数来查询 Map。缓存命中的本质就是一次成功的 Map 查找。如果查找失败(返回 NULL),则意味着缓存未命中,数据包将触发一次完整的策略查找或状态创建流程,这会增加延迟。

eBPF Map 查找流程:缓存命中 vs 未命中 数据包到达 bpf_map_lookup_elem(CT_Map, key) ✅ 缓存命中 直接使用缓存条目 ❌ 缓存未命中 触发完整策略查找 慢路径:延迟增加
排查要点: 缓存未命中通常由以下原因导致:
  • Map 大小不足: LRU Map 容量过小,导致活跃条目被过早淘汰。
  • 键值构造错误: 查找时使用的 Key 与存储时不一致(例如,IP 地址字节序错误)。
  • 程序逻辑缺陷: eBPF 程序未正确更新或删除 Map 中的条目。
使用 bpftool map dump 可以查看 Map 的当前内容,是定位此类问题的利器。

2.4 关键辅助函数

eBPF 程序不能直接调用内核函数,而是通过一组精心设计的辅助函数(Helper)与内核交互。以下是与 Cilium 缓存和网络处理最相关的几个函数:

  • bpf_map_lookup_elem(map, key):从 Map 中查找键对应的值。返回指向值的指针,若未找到则返回 NULL。
  • bpf_map_update_elem(map, key, value, flags):更新或创建 Map 中的条目。
  • bpf_map_delete_elem(map, key):从 Map 中删除一个条目。
  • bpf_skb_load_bytes(skb, offset, to, len):从网络数据包(SKB)中安全地读取数据到 eBPF 栈中。
  • bpf_ktime_get_ns():获取当前时间戳(纳秒),常用于计算缓存条目的老化时间。
性能基准: 一次成功的 bpf_map_lookup_elem 在 LRU 哈希表中的平均耗时通常在几十纳秒级别。如果排查中发现单次查找耗时超过 1 微秒,则可能存在 Map 冲突严重或 Per-CPU 数据局部性不佳的问题。