
云原生虚拟化存储后端高可用【免费下载链接】harvesterOpen source hyperconverged infrastructure (HCI) software项目地址https://gitcode.com/gh_mirrors/ha/harvester点击查看免费下载本篇技术指南以 vendor/github.com/cilium/ebpf/ARCHITECTURE.md 为核心骨架结合该 vendored 库的源码实现完整讲解 cilium/ebpf——一个纯 Go 实现的 eBPF 工具库——的内部架构与数据流。它被 Harvester 作为间接依赖随仓库发布见 go.mod 中的 replace 指令与 vendor/modules.txt理解其架构有助于你读懂任何依赖该库的 Go 网络、可观测性组件并为在自己的 Go 项目中加载、编译与调试 eBPF 程序打下坚实基础。读完后你将掌握该库从 ELF 字节码解析到内核对象装载、再到 hook 挂载的完整链路以及CollectionSpec、ProgramSpec、MapSpec、Program、Map、Link这些核心抽象之间的关系。总体架构一条单向数据管线库的架构被作者概括为一条四阶段流水线ELF - Specifications - Objects - LinksELF由 Clang 编译 BPF C 程序得到的 ELF 文件包含程序字节码与 map 元数据SpecificationsCollectionSpec、ProgramSpec、MapSpec等蓝图描述内核对象的全部信息ObjectsProgram、Map即真正加载进内核、持有文件描述符的对象LinksLink把已加载程序附着到内核各类 hook 上的句柄。下文按这四个阶段逐一展开并穿插源码级证据。第一阶段ELF——从 Clang 产物到解析BPF 程序通常由 Clang 编译 C 语言的子集产生。Clang 输出的 ELF 文件不仅包含程序字节码BPF还携带程序所用 map 的元数据。这些元数据遵循内核自带的 libbpf 约定ELF 中的特定 section 具有特殊含义其中包含由 libbpf 定义的结构而新版本 Clang 还会额外输出BPF Type FormatBTF元数据。库的设计目标是与 libbpf 兼容从而让从 C 工具链迁移到 Go 工具链的摩擦尽可能小。为此ELF 读取器 以 Linux selftests 为回归测试基准尽量避免引入自定义行为。ELF 读取器的输出是一个CollectionSpec——它以 Go 中易于操作的形式编码了 ELF 携带的全部信息。源码视角section 如何被识别在 elf_reader.go 中入口函数LoadCollectionSpec以及面向io.ReaderAt的LoadCollectionSpecFromReader负责把 ELF 文件解析为CollectionSpec。解析过程中按 section 名称与属性分类见 elf_reader.goELF section / 特征含义license前缀BPF 程序许可证version前缀BPF 程序版本maps前缀传统 libbpf map 定义.maps基于 BTF 的 map 定义.bss、.data、.rodata前缀全局数据段数据区 sectionSHT_REL类型重定位节按目标 section 索引归类稍后解析SHT_PROGBITS且带SHF_EXECINSTR且大小非零程序指令section解析完成后许可证license、版本version、BTF 规范btf.LoadSpecFromReader以及符号表f.Symbols()被逐一加载并绑定到对应 section 上。可以看到架构文档所说的某些 ELF section 具有特殊含义在实现层面就是上表中的分类逻辑。BTF超越类型的类型格式BPF Type FormatBTF描述的远不止 BPF 程序使用的类型它还包括调试辅助信息比如哪条源码行对应哪条指令、使用了哪些全局变量等。internal/btf 包与 btf.TypeBTF 解析 被放在独立的 internal 子包中原因有二对外暴露它意味着额外的维护负担且该 API 仍存在尖锐的边角问题。这个包最重要的概念是btf.Type接口——见 internal/btf/types.go它定义了ID()、String()、copy()与walk()四个方法。值得注意的是btf.Type也描述那些并非真正类型的东西例如.rodata或.bss这样的数据段 section。在实现中它们对应Datasec数据段类型其成员是Var全局变量——types.go 的接口断言清单即包含(*Datasec)(nil)与(*Var)(nil)。btf.Type之间可以形成循环引用图如果不小心很容易陷入无限循环。作者坦言希望随着代码量的增长能沉淀出一套安全处理btf.Type图的通用模式——这既是架构文档的原话也是该包仍处于 internal 状态的原因之一。第二阶段Specifications——内核对象的蓝图CollectionSpec、ProgramSpec与MapSpec是内核对象的蓝图blueprint包含执行相关bpf(2)系统调用所需的全部信息。collection.go 中的CollectionSpec持有Maps map[string]*MapSpec、Programs map[string]*ProgramSpec以及ByteOrderELF 编译目标的字节序。map.go 中的MapSpec定义了Name、Typemap 类型、KeySize、ValueSize、MaxEntries、Flags、Pinning、NumaNode、初始内容Contents、是否冻结Freeze、内层 map 模板InnerMap以及关联的BTF等字段。prog.go 中的ProgramSpec定义了Name、Type决定程序在内核哪个 hook 运行、AttachType、AttachTo、AttachTarget、Instructions asm.Instructions、Flags与License部分 helper 仅在许可证与 GPL 兼容时才可用。因为 ELF 读取器输出的是CollectionSpec所以可以在加载前修改 Clang 编译好的 BPF 代码例如重写常量CollectionSpec.RewriteConstants见 collection.go用于替换多个常量的值。C 侧需按volatile const type foobar;方式声明替换值长度必须与sizeof(type)一致从 Linux 5.5 起verifier 会利用这些常量消除死代码。CollectionSpec.RewriteMaps见 collection.go可用预先存在的 map 替换新建 map替换后对应符号会从CollectionSpec.Maps中移除。与此同时asm 包提供了 eBPF 字节码汇编器见 asm/doc.go可在运行时直接生成ProgramSpec无需依赖 C 工具链。创建 spec 这一过程不应需要任何特权也不应受任何限制例如只允许原生字节序的程序。这一设计约束保证了库的灵活性spec 阶段是纯用户态操作权限检查统一推迟到对象加载阶段。此外CollectionSpec还提供便捷方法Assign与LoadAndAssign见 collection.go通过 struct 字段上的ebpf:程序名/映射名标签把 spec 中的对象批量赋值到 Go 结构体LoadAndAssign还会在赋值前把依赖资源懒加载进内核。第三阶段Objects——把蓝图加载进内核Program与Map是把 spec 加载进内核后的结果对象。但加载可能失败因为内核太老、或某特性未启用。库用两种机制应对回退Fallback老内核不允许为程序与 map 命名。库会自动探测内核是否支持命名必要时在加载时省略名称。这之所以可行是因为名字主要只是调试辅助手段。哨兵错误Sentinel error有时可以预先判定某特性不可用此时库返回一个包装了ErrNotSupported的错误见 prog.go 中对internal.ErrNotSupported的再导出。这同样可用于跳过无法在当前内核上运行的测试。底层 API 与安全封装一旦Program与Map对象加载完成它们便暴露内核的低层 API例如Map.NextKey。这类 API 在 Go 中往往难以使用因此库在其之上提供了更安全的封装如MapIterator。低层 API 的价值在于当高层 API 不支持某个特定用例时开发者仍然可以直接操作。在 collection.go 中Collection即Programs与Maps的集合NewCollection/NewCollectionWithOptions负责按依赖顺序先 map 后 program因为 map 的 fd 需要被嵌入程序把整个 spec 实例化为内核对象。第四阶段Links——把程序挂到内核 hook 上BPF 可以附着到内核中许多不同的位置。较新的 BPF hook 倾向于使用bpf_link完成附着较老的 hook 则使用系统调用、netlink 消息等的组合。架构文档明确划定了边界为新增 link 类型提供支持时不应引入 netlink 这类重量级依赖因此XDP 程序或 tracepoint 不在本库当前支持范围之内这类附着需要 netlink 交互。link 子包与 Link 接口link 子包封装了各种 hook 的附着逻辑从该包的文件清单可以看出覆盖的 attach 类型kprobe.go 与 uprobe.go内核/用户态动态探针tracepoint.go 与 raw_tracepoint.go跟踪点cgroup.go、netns.go、freplace.gocgroup、网络命名空间、函数替换fentry/fexit 类等perf_event.goperf 事件。Link接口见 link/link.go定义了一组生命周期操作Update(*ebpf.Program)用新程序替换当前程序传入 nil 会报错Pin(string)把 link 固定pin进 bpffs 以持久化Unpin()撤销之前的 PinClose()释放资源。注意除非 link 已被成功 pin否则 Close 会断开附着反过来若不调用 Closelink 在进程退出后仍可能继续存活。RawLinkOptions同文件 link/link.go则直接暴露了底层bpf_link创建所需的Target附着目标 fd、Program、Attach与BTF参数。在本仓库中的位置与版本cilium/ebpf 以 vendored 依赖的形式随 Harvester 仓库分发位于 vendor/github.com/cilium/ebpf顶层含collection.go、elf_reader.go、map.go、prog.go、asm/、link/、internal/btf/等文件。版本约束见 go.mod 第 6 行的 replace 指令github.com/cilium/ebpf github.com/cilium/ebpf v0.7.0以及 vendor/modules.txt 中列出的asm、internal、internal/btf、internal/unix、link等子包清单。从仓库源码结构看Harvester 自身业务代码并未直接 import 该库它作为间接依赖被引入go.mod 中标注为// indirect但其四阶段架构与兼容 libbpf 的设计对任何上游依赖方包括网络与可观测性组件的二次开发都具有直接参考价值。小结cilium/ebpf 以ELF → Specifications → Objects → Links四条阶段串起 eBPF 的完整生命周期ELF 阶段借助 libbpf 约定与 BTF 完成格式解析Specifications 阶段以纯用户态、无特权的方式提供可修改、可生成的蓝图Objects 阶段通过回退与ErrNotSupported哨兵错误优雅应对内核差异Links 阶段则用统一的Link接口管理附着生命周期。理解这条管线是深入使用该库无论是直接加载 Clang 产物还是用asm包在 Go 中手写字节码的起点。赞分享云原生虚拟化存储后端高可用【免费下载链接】harvesterOpen source hyperconverged infrastructure (HCI) software项目地址https://gitcode.com/gh_mirrors/ha/harvester点击查看免费下载相关推荐逆向 Agent 工具 REA一句话查清应用的登录、存储与更新逆向 Agent 工具 REA一句话查清应用的登录、存储与更新 没拿到源码的应用想弄清三件事登录请求怎么走、用户数据落在哪、两个版本之间改了什么。传统做法逆向工程MCP 服务AI 技能Valhalla 路由引擎开发者指南从行星级 Tile 构建到路由流水线的完整技术地图Valhalla 路由引擎开发者指南从行星级 Tile 构建到路由流水线的完整技术地图 导读 本文是一份面向 Valhalla 项目开发者尤其是 AI 编程后端Cilium cilium-dbg bpf sha 命令详解管理编译后的 BPF 模板对象Datapath SHACilium cilium dbg bpf sha 命令详解管理编译后的 BPF 模板对象Datapath SHA 导读 cilium dbg bpf s云原生网络服务网格可观测性网络安全eBPF创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考