2、eBPF基础:eBPF架构、eBPF程序类型、eBPF Map与Helper函数
2.1 eBPF 整体架构
eBPF(extended Berkeley Packet Filter)是一种在内核中运行沙箱化程序的技术,无需修改内核源码或加载内核模块即可安全地扩展内核能力。其核心架构由用户空间、内核空间以及验证器/即时编译器(JIT)三部分组成。
架构要点: eBPF 程序通过
bpf() 系统调用从用户态加载到内核,经过验证器(Verifier)确保安全后,由 JIT 编译为原生机器码执行。程序通过 eBPF Map 与用户态交换数据,通过 Helper 函数访问内核功能。
2.2 eBPF 程序类型
eBPF 支持多种程序类型,每种类型绑定到特定的内核事件或钩子点。在 Hubble 流量可视化场景中,最常用的是 XDP、TC、kprobe 和 tracepoint 类型。
| 程序类型 | 挂载点 | 触发时机 | Hubble 用途 |
|---|---|---|---|
| XDP | 网卡驱动层 | 数据包到达网卡时(最早) | 高性能包过滤、负载均衡 |
| TC(流量控制) | 内核网络协议栈 | 数据包进出协议栈时 | 包标记、转发、丢弃统计 |
| kprobe | 内核函数入口/出口 | 内核函数被调用时 | 跟踪系统调用、网络事件 |
| tracepoint | 内核静态跟踪点 | 预定义内核事件发生时 | 稳定可靠的网络事件采集 |
| cgroup 套接字 | cgroup 层级 | 进程套接字操作时 | 容器级网络策略与监控 |
| perf_event | 性能计数器 | 硬件/软件性能事件 | 性能指标采集 |
实践建议: 在 Cilium/Hubble 中,XDP 和 TC 程序负责数据面包处理,tracepoint 和 kprobe 用于控制面事件采集。配额校准场景通常使用 TC 程序在出口方向统计流量,结合 tracepoint 跟踪连接生命周期。
2.3 eBPF Map 与数据交换
eBPF Map 是内核与用户空间之间、以及不同 eBPF 程序之间共享数据的核心机制。Map 以键值对形式存储,支持多种数据类型。
常用 Map 类型
| Map 类型 | 语义 | 典型用途 |
|---|---|---|
| BPF_MAP_TYPE_HASH | 哈希表 | 连接跟踪表、策略规则表 |
| BPF_MAP_TYPE_ARRAY | 数组 | 统计计数器、固定配置 |
| BPF_MAP_TYPE_PERCPU_HASH | Per-CPU 哈希 | 高性能统计,避免锁竞争 |
| BPF_MAP_TYPE_PERCPU_ARRAY | Per-CPU 数组 | 逐 CPU 计数器 |
| BPF_MAP_TYPE_LRU_HASH | LRU 哈希 | 有限大小的连接缓存 |
| BPF_MAP_TYPE_RINGBUF | 环形缓冲区 | 高效事件推送(Hubble 事件流) |
Map 操作示例
// 定义 Map(内核态)
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 65536);
__type(key, struct ip_key);
__type(value, struct flow_stats);
} flow_map SEC(".maps");
// 内核态更新 Map
static __always_inline void update_flow(struct ip_key *key, struct flow_stats *stats) {
bpf_map_update_elem(&flow_map, key, stats, BPF_ANY);
}
// 用户态读取 Map(通过 libbpf)
struct flow_stats *value = bpf_map_lookup_elem(bpf_obj->maps.flow_map, &key);
if (value) {
printf("bytes: %lu\n", value->bytes);
}
2.4 Helper 函数
eBPF Helper 函数是内核提供给 eBPF 程序调用的预定义接口,用于访问内核数据结构、操作网络包、管理 Map 等。Helper 函数保证了 eBPF 程序的安全性和可移植性。
常用 Helper 函数分类
| 分类 | 函数示例 | 功能描述 |
|---|---|---|
| Map 操作 | bpf_map_lookup_elembpf_map_update_elembpf_map_delete_elem |
Map 的增删改查 |
| 包数据访问 | bpf_skb_load_bytesbpf_skb_store_bytesbpf_skb_pull_data |
读取/修改网络包数据 |
| 时间与统计 | bpf_ktime_get_nsbpf_get_current_pid_tgidbpf_get_current_comm |
获取时间戳、进程信息 |
| 网络转发 | bpf_redirectbpf_redirect_mapbpf_clone_redirect |
数据包重定向 |
| 事件通知 | bpf_perf_event_outputbpf_ringbuf_output |
向用户态推送事件 |
| 辅助功能 | bpf_get_prandom_u32bpf_trace_printk |
随机数、调试输出 |
注意事项: Helper 函数的可用性取决于 eBPF 程序类型。例如,
bpf_redirect 只能在 XDP 和 TC 程序中使用;bpf_perf_event_output 在大多数程序类型中可用,但参数格式不同。编写程序前应查阅内核版本对应的 Helper 函数列表。
配额校准场景中的典型 Helper 使用
// TC 出口方向统计流量
SEC("tc/egress")
int tc_egress(struct __sk_buff *skb) {
struct ip_key key = {};
struct flow_stats *stats;
// 提取 IP 五元组
bpf_skb_load_bytes(skb, ETH_HLEN, &key, sizeof(key));
// 查找连接跟踪表
stats = bpf_map_lookup_elem(&flow_map, &key);
if (!stats) {
struct flow_stats new = {0};
bpf_map_update_elem(&flow_map, &key, &new, BPF_NOEXIST);
stats = bpf_map_lookup_elem(&flow_map, &key);
if (!stats) return TC_ACT_OK;
}
// 更新字节计数(原子操作)
__sync_fetch_and_add(&stats->bytes, skb->len);
// 推送事件到用户态
bpf_ringbuf_output(&event_ring, &key, sizeof(key), 0);
return TC_ACT_OK;
}
2.5 小结
- eBPF 架构:用户态加载 → 验证器安全检查 → JIT 编译 → 内核钩子执行 → Map/事件回传。
- 程序类型:XDP/TC 用于数据面,tracepoint/kprobe 用于控制面,cgroup 用于容器级策略。
- Map 机制:Hash/Array/Per-CPU/Ringbuf 等类型满足不同性能与语义需求。
- Helper 函数:提供安全的内核接口,是 eBPF 程序与内核交互的唯一途径。