-
eBPF 子系统是什么?
eBPF 子系统是 Linux 内核的一个核心功能模块。
- 内核组成部分:它以代码的形式嵌入在内核源码中,作为 Linux 内核编译和运行时的一部分。
- 功能模块:提供一种机制,使得用户可以动态扩展内核功能,而无需重新编译或加载模块。
- 运行环境:eBPF 子系统随着内核启动加载,并始终作为内核的一部分运行。
关键特点
- 嵌入式:eBPF 是内核的原生能力,而非独立运行的服务或进程。
- 动态扩展:通过用户加载 eBPF 程序,eBPF 子系统支持对内核行为进行定制。
- 安全可靠:运行 eBPF 程序时会经过严格的验证,确保不会影响内核稳定性。
-
eBPF 子系统的存活方式
eBPF 子系统在编译、启动和运行时的生存状态如下:
2.1 作为内核的一部分
编译阶段:
- eBPF 子系统的代码主要位于
kernel/bpf/和相关子模块中。 - 当编译 Linux 内核时,eBPF 子系统代码被编译为内核镜像(如
vmlinuz)。 - 如果内核编译选项中启用了 eBPF 功能(现代内核默认启用),相关功能模块会被内核构建。
启动阶段:
-
当 Linux 系统启动时,eBPF 子系统会被初始化:
- 加载 eBPF 核心组件(如虚拟机、验证器、JIT 编译器等)。
- 注册系统调用接口
bpf(),允许用户通过接口与子系统交互。 - 初始化 eBPF Maps 支持,用于数据存储和共享。
-
这一过程由内核代码自动完成,无需用户干预。
运行时:- eBPF 子系统与内核紧密耦合,作为内核态的功能模块运行。
- 它始终驻留在内存中,等待用户加载 eBPF 程序。
- 用户可以通过工具(如 bpftool)或
bpf()系统调用向内核加载 eBPF 程序。
2.2 为什么 eBPF 子系统不是进程?- 内核态模块:
- eBPF 子系统运行在内核空间中,与调度器、网络栈等模块类似。 - 它不会以用户空间进程的形式存在,而是内核功能的一部分。-
共享内核 地址空间:
- 作为内核代码的一部分,eBPF 子系统与其他模块共享内核内存。
- 这种设计避免了用户态与内核态之间的上下文切换,性能更高效。
-
始终驻留内核:
- 只要内核在运行,eBPF 子系统就会存在,随时响应用户请求。
- eBPF 子系统的代码主要位于
-
eBPF 子系统的存在状态
eBPF 子系统的核心组件包括:
-
eBPF ****虚拟机:
- 提供执行环境,解释和运行 eBPF 字节码。
-
Verifier(验证器) :
- 在加载 eBPF 程序时,验证代码的安全性和合法性。
-
JIT 编译器:
- 将 eBPF 字节码翻译为本地机器代码,提高运行效率。
-
挂载点:
- 支持 eBPF 程序挂载到特定内核事件(如 Kprobe、Tracepoint)。
-
BPF Maps 支持:
- 提供内核态与用户态之间的数据存储和交换机制。
-
-
eBPF 子系统的运行过程
系统启动后的状态:- eBPF 子系统已经初始化,但不会主动运行。
bpf()系统调用接口已准备好,用户可加载程序。
加载 eBPF 程序后的状态:- 用户通过工具或 API 加载 eBPF 程序。
- eBPF 子系统接收请求,验证并加载程序。
- 程序被挂载到某个事件触发点(如网络包处理或内核跟踪点)。
- 一旦触发事件,eBPF 程序会动态运行。
-
eBPF 子系统的“存活”总结
-
内核启动时加载:
- eBPF 子系统随着 Linux 内核一起加载,无需用户额外操作。
-
作为内核功能存在:
- 它是内核功能模块,不是用户态的独立进程。
-
按需运行:
- 没有加载 eBPF 程序时,子系统是静态存在的,不消耗额外资源。
- 加载 eBPF 程序后,它会动态激活,根据触发条件执行。
-
始终驻留内核:
- 只要内核运行,eBPF 子系统就处于“随时待命”状态。
-
-
示例:eBPF 子系统的行为
初始状态:
-
系统启动后,eBPF 子系统完成初始化:
- 虚拟机、验证器、JIT 编译器已加载,但未运行任何程序。
bpf()系统调用已注册,但尚未被调用。
动态加载程序后:
-
用户通过工具加载一个 eBPF 程序(如
bpf_prog_load())。 -
eBPF 子系统:
- 验证程序合法性。
- 将程序挂载到指定的触发点(如网络路径)。
- 触发事件时执行该程序。
-
-
类比总结
eBPF 子系统可以类比为一个工厂的自动化区域:
- 工厂:Linux 内核。
- eBPF 子系统:工厂中的一个专门功能区域,默认处于待机状态。
- eBPF 程序:用户提交的任务,只有需要时才激活功能区域。
这种设计使得 eBPF 子系统高效且灵活,能够随时响应用户需求,同时最大化系统资源利用率。