土法炼钢兴趣小组的算法知识备份

【eBPF 内核实现深度拆解】BTF 格式规范与内核类型系统

文章导航

分类入口
kernelebpf
标签入口
#ebpf#btf#type-system#co-re#pahole#btf_dedup#func-info#line-info#linux-kernel

目录

如果你曾经在内核模块里用过 container_of(),就知道 C 代码依赖编译期的类型信息——offsetof(struct task_struct, pid) 在编译时就已经固定。BPF 程序在内核中运行时没有 libc、没有头文件、没有 <stddef.h>,但它需要读 struct task_struct 的某个字段、需要知道 struct socksk_stateunsigned char 还是 int、需要理解一个函数签名来判断 helper 调用的类型匹配。

这些信息都来自 BTF(BPF Type Format)——一种为 BPF 定制的二进制类型编码格式。它小到可以放在内核镜像里(vmlinux BTF 经 btf_dedup() 去重后通常在 1–2 MB 量级;下文涉及 vmlinux 体积的数字均来自典型 x86_64 构建且开启 CONFIG_DEBUG_INFO_BTF 时的经验值,具体大小因内核配置和架构而异),紧凑到可以快速解析(单遍扫描),表达能力足够覆盖 C 语言的类型系统(结构体、联合体、枚举、函数原型、typedef、类型修饰符)。

本文从 struct btf_header 的二进制布局开始,逐步拆解 20 种 BTF type kind 的编码方式,讲清 BTF.ext 的 func_info 和 line_info 如何连接指令偏移与类型信息,最后追踪内核构建流程中 pahole 的 DWARF-to-BTF 转换和 btf_dedup() 的去重算法。源码基于 kernel 6.6 的 include/uapi/linux/btf.htools/lib/bpf/btf.c

一、为什么 BTF 取代了 DWARF

DWARF(Debugging With Attributed Record Formats)是 C/C++ 编译器生成的标准调试信息格式。Linux 内核的 CONFIG_DEBUG_INFO=y 会在 vmlinux 中生成 DWARF 调试信息。但 DWARF 在 BPF 场景下有三个硬伤:

维度 DWARF BTF
大小 vmlinux DWARF 可达 ~120 MB(典型 x86_64 + CONFIG_DEBUG_INFO_BTF 构建) vmlinux BTF ~1–2 MB(去重后,同上构建条件)
解析复杂度 需要处理复杂的 DIE 树、跨 CU 引用、属性缩写 单一结构体数组,单遍线性扫描
内核内解析 不可行(内存占用 + 复杂度) 可行(kernel/bpf/btf.c 在加载时解析)
表示能力 C/C++ 所有特性 C 语言子集(足以覆盖内核类型系统)
自包含性 需要调试信息解析库(libdw/libdwarf) 自描述文件头,无需外部库

BPF 需要类型信息在内核中可用——verifier 通过 BTF 验证 fentry 的 callback 签名、bpftool map dump 通过 BTF 将 map value 人性化展示、CO-RE 重定位通过 BTF 跨内核版本计算字段偏移。这些场景都要求一个比 DWARF 小 100 倍、解析简单得多的格式。

BTF 的设计目标不是通用的调试格式,而是 BPF 程序的元数据格式。它只编码 BPF 程序需要的那部分类型信息:基本类型的大小和符号属性、复合类型的成员偏移、函数签名、类型名称映射。

二、BTF 二进制布局

BTF 在 ELF 文件中是一个独立的 section(.BTF),在内存中是一个连续字节块。整个格式由三个顺序部分构成:header、type section、string section。

graph TD
    subgraph BTF_FILE["BPF ELF .o 文件中的 .BTF 节"]
        HDR["struct btf_header<br/>magic: 0xeb9f<br/>version: 1<br/>hdr_len: 24<br/>type_off: 24<br/>type_len<br/>str_off<br/>str_len"]
        TYPES["Type Section<br/>struct btf_type 数组<br/>每个 12 字节<br/>可能后跟 variable-length data"]
        STRINGS["String Section<br/>\0 分隔的字符串表<br/>type entries 以 offset 引用"]
    end
    HDR --> TYPES --> STRINGS

2.1 btf_header

struct btf_header 定义在 include/uapi/linux/btf.h,固定 24 字节(按 32-bit 对齐),出现在 BTF 数据块的最前部:

/* include/uapi/linux/btf.h (kernel 6.6) */
struct btf_header {
    __u16   magic;      /* 魔数:0xeb9f */
    __u8    version;    /* 版本:当前为 1 */
    __u8    flags;      /* 标志位,当前为 0 */
    __u32   hdr_len;    /* sizeof(struct btf_header) + 可变长度数据(若有) */
    __u32   type_off;   /* 类型节的起始偏移(从 BTF 数据起始算起) */
    __u32   type_len;   /* 类型节的字节长度 */
    __u32   str_off;    /* 字符串节的起始偏移 */
    __u32   str_len;    /* 字符串节的字节长度 */
};

关键语义:

2.2 btf_type

每个类型以 struct btf_type 12 字节的固定头部开始,后面可能跟随变长数据(成员列表、枚举值列表、参数列表等):

/* include/uapi/linux/btf.h (kernel 6.6) */
struct btf_type {
    __u32 name_off;     /* 类型名称在字符串表中的偏移(0 表示匿名类型) */
    __u32 info;         /* 编码:kind + vlen + kind_flag */
    __u32 size;         /* 类型大小(结构体/联合体/枚举等),或 "type" 字段 */
};

info 字段的位布局需要解码:

/* tools/lib/bpf/btf.h */
#define BTF_INFO_KIND(info)     (((info) >> 24) & 0x1f)   /* bits 24-28: kind (5 bits) */
#define BTF_INFO_VLEN(info)     ((info) & 0xffff)          /* bits 0-15: vlen (16 bits) */
#define BTF_INFO_KFLAG(info)    ((info) >> 31)             /* bit 31: kind_flag */

size 字段的含义随 kind 变化:

kind size 含义
BTF_KIND_INT 整数类型的总字节大小
BTF_KIND_STRUCT / BTF_KIND_UNION 结构体/联合体的总字节大小
BTF_KIND_ENUM / BTF_KIND_ENUM64 枚举的底层类型大小
BTF_KIND_PTR / BTF_KIND_FWD / BTF_KIND_TYPEDEF 0(不适用)
BTF_KIND_ARRAY 0,数组的 element type / nelems 存在 vlen data 中
BTF_KIND_FUNC <linkage>(符号链接属性)
BTF_KIND_FUNC_PROTO 无关紧要
BTF_KIND_VAR 变量大小
BTF_KIND_DATASEC 节的字节大小

2.3 字符串表

字符串表是一个以 \0 结尾的连续字节区域,存放在 BTF 数据块的尾部。name_off 是类型名称在字符串表中的起始偏移——与 ELF 的字符串表不同,BTF 字符串没有全局去重的要求(虽然 btf_dedup 会合并相同字符串),解析时按 name_off 直接读取,读到下一个 \0 为止。

例如,name_off = 42 意味着类型名称的起始地址为 btf_data + str_off + 42

三、20 种 BTF Type Kind 编码

3.1 基本类型:BTF_KIND_INT

BTF_KIND_INT 编码 C 语言的整数类型(char, short, int, long, unsigned long, bool 等)。size 字段给出字节大小(1/2/4/8),vlen data 包含一个 32-bit 的 encoding 字段:

/* include/uapi/linux/btf.h - BTF_INT_* 编码位 */
#define BTF_INT_ENCODING(VAL)   (((VAL) & 0x0f000000) >> 24)
#define BTF_INT_OFFSET(VAL)     (((VAL) & 0x00ff0000) >> 16)
#define BTF_INT_BITS(VAL)       ((VAL)  & 0x000000ff)

/* 编码标志 */
#define BTF_INT_SIGNED  (1 << 0)  /* 有符号 */
#define BTF_INT_CHAR    (1 << 1)  /* char 或类似 */
#define BTF_INT_BOOL    (1 << 2)  /* bool */

例子——unsigned int(32-bit 无符号整数): - name_off 指向字符串 "unsigned int" - infokind=BTF_KIND_INT, vlen=1(1 个 encoding 值) - size=4 - vlen data 中的 encoding 值:bits(32) | offset(0) | (BTF_INT_SIGNED 未置位)

3.2 指针/引用:BTF_KIND_PTR

最简单的一种——size 为 0(真正的类型信息在被指向的类型中),vlen 为 0,vlen data 为空。指向目标类型的 type_id 存放在 btf_type->size 后面的隐含 4 字节中:

struct btf_type    // 12 字节
__u32 type;        // 隐含的 4 字节:被指向类型的 type_id

解析器需要根据 btf_type 的长度判断是否有这个尾部字段——BTF_KIND_PTRvlen=0 且 total size = 16(12 + 4)。

3.3 数组:BTF_KIND_ARRAY

编码 C 语言的数组(int arr[10])。size 字段为 0。vlen data 包含一个 struct btf_array

/* include/uapi/linux/btf.h (kernel 6.6) */
struct btf_array {
    __u32   type;           /* 元素类型 ID */
    __u32   index_type;     /* 索引类型 ID(通常是 'unsigned int' 的 BTF type_id) */
    __u32   nelems;         /* 元素个数 */
};

对于 int[5]type 指向 int 类型的 BTF type_id,index_type 指向 __u32 对应的 BTF_KIND_INT entry,nelems=5

3.4 结构体:BTF_KIND_STRUCT

编码 C 语言的结构体。size 字段给出总字节大小。vlen 表示成员数量,vlen data 包含 vlenstruct btf_member

/* include/uapi/linux/btf.h (kernel 6.6) */
struct btf_member {
    __u32   name_off;       /* 成员名称在字符串表中的偏移 */
    __u32   type;           /* 成员类型 ID */
    __u32   offset;         /* 从结构体起始到该成员的位偏移 */
};

offset 的单位是(bit),不是字节。对于正常的非位域成员,offset 是字节偏移量乘以 8。对于位域成员,offset 的低 24 位表示位偏移,高 8 位表示位域大小(当 kind_flag 设置时):

offset 编码(kind_flag=1 时):
  bits [31:24]: 位域大小(0 表示非位域)
  bits [23:0]:  位偏移(从结构体起始)

3.5 联合体:BTF_KIND_UNION

BTF_KIND_STRUCT 几乎相同,唯一的区别是所有成员的 offset 都是 0(因为联合体成员共享起始地址),size 是最大成员的大小。

3.6 枚举:BTF_KIND_ENUM 与 BTF_KIND_ENUM64

编码 C 语言的枚举。vlen data 包含 vlenstruct btf_enumstruct btf_enum64

/* include/uapi/linux/btf.h */
struct btf_enum {
    __u32   name_off;       /* 枚举值名称 */
    __s32   val;            /* 枚举值(32-bit 有符号) */
};

struct btf_enum64 {
    __u32   name_off;
    __u32   val_lo32;       /* 低 32-bit */
    __u32   val_hi32;       /* 高 32-bit */
};

BTF_KIND_ENUM64(6.0+)解决了传统 BTF_KIND_ENUM 无法表示 64-bit 枚举值的问题——在内核中带有 U64_MAX 等值的枚举需要 64 位宽度。

3.7 前向声明:BTF_KIND_FWD

编码 C 语言的前向声明(struct task_struct;)。name_off 指向类型名称,size=0(前向声明没有大小信息),kind_flag 指示是 struct(0)还是 union(1)。

3.8 typedef:BTF_KIND_TYPEDEF

编码 C 语言的 typedef(typedef int pid_t)。size=0,隐含的尾部 __u32 type 指向被定义的类型。

3.9 类型修饰符:CONST / VOLATILE / RESTRICT / TYPE_TAG

这四种修饰符都是对另一个类型的包裹:

编码方式与 BTF_KIND_PTR 相同——隐含的尾部 __u32 type 指向被修饰的类型,name_off 指向类型标签的名称(如 "__user")。

3.10 函数相关:BTF_KIND_FUNC 与 BTF_KIND_FUNC_PROTO

BTF_KIND_FUNC 编码内核中的函数符号(如 tcp_connect)。name_off 指向函数名,隐含的尾部 __u32 type 指向对应的 BTF_KIND_FUNC_PROTO

BTF_KIND_FUNC_PROTO 编码函数签名。size 表示参数数量。vlen data 包含 vlenstruct btf_param,每个参数为一个 (name_off, type_id) 对:

/* include/uapi/linux/btf.h */
struct btf_param {
    __u32   name_off;       /* 参数名称(可以是 0 表示未命名) */
    __u32   type;           /* 参数类型 ID */
};

隐含的尾部 __u32 type 指向返回类型。

int tcp_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) 在 BTF 中编码为:

FUNC_PROTO(返回类型=INT, 参数数=3)
  param[0]: name="sk", type=PTR→STRUCT("sock")
  param[1]: name="uaddr", type=PTR→STRUCT("sockaddr")
  param[2]: name="addr_len", type=INT
FUNC(name="tcp_connect", type=FUNC_PROTO)

3.11 全局变量与节:BTF_KIND_VAR 与 BTF_KIND_DATASEC

BTF_KIND_VAR 编码全局变量(如 BPF 程序的全局 data/rodata 变量)。name_off 指向变量名,隐含尾部 __u32 type 指向变量类型,额外的尾部 __u32 linkage(1 = static,2 = global,3 = extern)。

BTF_KIND_DATASEC 编码 ELF Section 中的变量布局。size 是节的字节大小,vlen data 包含 vlenstruct btf_var_secinfo

/* include/uapi/linux/btf.h */
struct btf_var_secinfo {
    __u32   type;       /* BTF_KIND_VAR 的 type_id */
    __u32   offset;     /* 变量在节中的字节偏移 */
    __u32   size;       /* 变量占用的字节大小 */
};

skeleton 中的 skel->rodataskel->data 等指针就是通过 DATASEC 解析得到的——它告诉用户态代码每个全局变量在 BPF 程序的数据区中的精确偏移和大小。

3.12 浮点与声明标签

3.13 完整的 type kind 表

kind 枚举 数值 引入版本 vlen data 结构体 用途
BTF_KIND_INT 1 本源 u32 encoding 整数、char、bool
BTF_KIND_PTR 2 本源 u32 type 指针
BTF_KIND_ARRAY 3 本源 struct btf_array 数组
BTF_KIND_STRUCT 4 本源 struct btf_member[] 结构体
BTF_KIND_UNION 5 本源 struct btf_member[] 联合体
BTF_KIND_ENUM 6 本源 struct btf_enum[] 枚举(32-bit)
BTF_KIND_FWD 7 本源 前向声明
BTF_KIND_TYPEDEF 8 本源 u32 type typedef
BTF_KIND_VOLATILE 9 本源 u32 type volatile 修饰
BTF_KIND_CONST 10 本源 u32 type const 修饰
BTF_KIND_RESTRICT 11 本源 u32 type restrict 修饰
BTF_KIND_FUNC 12 5.0 u32 type 函数符号
BTF_KIND_FUNC_PROTO 13 5.0 struct btf_param[] 函数签名
BTF_KIND_VAR 14 5.1 u32 type + u32 linkage 全局变量
BTF_KIND_DATASEC 15 5.1 struct btf_var_secinfo[] 节内变量布局
BTF_KIND_FLOAT 16 5.1 浮点类型
BTF_KIND_DECL_TAG 17 5.16 u32 type + s32 idx 声明注解
BTF_KIND_TYPE_TAG 18 5.17 u32 type 类型标签
BTF_KIND_ENUM64 19 6.0 struct btf_enum64[] 枚举(64-bit)

四、BTF.ext 节:连接指令与类型

.BTF.ext 是 ELF 文件中独立于 .BTF 的节,包含三类扩展信息:

/* include/uapi/linux/btf.h */
struct btf_ext_header {
    __u16   magic;          /* 0xeb9f */
    __u8    version;
    __u8    flags;
    __u32   hdr_len;
    __u32   func_info_off;  /* func_info 子节的偏移 */
    __u32   func_info_len;
    __u32   line_info_off;  /* line_info 子节的偏移 */
    __u32   line_info_len;
    __u32   core_relo_off;  /* CO-RE 重定位子节的偏移(第 12 篇详述) */
    __u32   core_relo_len;
};

4.1 func_info

func_info 将 BPF 程序中的函数指令偏移映射到 BTF 中的函数类型 ID。内核 verifier 在加载时使用 func_info 来确定 BPF_PSEUDO_CALL 目标函数的签名——这使得 verifier 能够像检查 helper 调用一样检查程序内调用:

/* 对每个 BPF 子程序 */
struct bpf_func_info {
    __u32   insn_off;       /* 函数起始指令的偏移(从程序第一条指令算起) */
    __u32   type_id;        /* 此函数在 BTF 中的 BTF_KIND_FUNC type_id */
};

4.2 line_info

line_info 将 BPF 程序的每条指令映射回源码的 "file:line:column\“。它的作用是让 verifier 日志输出人类可读的源码位置,而不是只打印指令编号:

/* 对每条 BPF 指令 */
struct bpf_line_info {
    __u32   insn_off;       /* 指令偏移 */
    __u32   file_name_off;  /* 文件名在 BTF 字符串表中的偏移 */
    __u32   line_off;       /* 行号在 BTF 字符串表中的偏移(存为字符串) */
    __u32   line_col;       /* 低 10 位: 列号, 高 22 位: 行号 */
};

没有 line_info 时,verifier 日志长这样:

0: (b7) r1 = 0
1: (73) *(u8 *)(r10 -1) = r1
2: (bf) r2 = r10
...

line_info 后:

; u8 key = 0;
0: (b7) r1 = 0
; map_val = bpf_map_lookup_elem(&my_map, &key);
1: (73) *(u8 *)(r10 -1) = r1
2: (bf) r2 = r10

这在调试大型 BPF 程序时是不可或缺的。

4.3 CO-RE 重定位条目

core_relo 子节存储面向 CO-RE 的重定位条目(struct bpf_core_relo),包含指令偏移、访问路径、重定位种类等信息。libbpf 在加载时使用这些条目修补 BPF 指令。详细机制见第 12 篇。

五、内核 BTF 的生成管线

5.1 构建流程

内核 BTF 的生成是构建流程的一部分,需要在 CONFIG_DEBUG_INFO_BTF=y 下进行:

kernel compile (gcc/clang + -g)
   vmlinux(含 DWARF .debug_info 节, ~120 MB)
     pahole --btf_encode_detached
       vmlinux.btf(BTF 编码, ~5 MB 原始)
         libbpf btf_dedup()
           vmlinux.btf(去重后, ~1–2 MB,典型 x86_64 + CONFIG_DEBUG_INFO_BTF)
             嵌入 vmlinux image
               挂载到 /sys/kernel/btf/vmlinux

关键步骤:

  1. 内核编译gcc -gclang -g 生成 DWARF 调试信息,嵌入 vmlinux ELF;
  2. pahole 转换:构建系统(scripts/link-vmlinux.sh)调用 pahole --btf_encode_detached,从 vmlinux 的 DWARF 中提取类型信息,编码为 BTF 格式;
  3. btf_dedup 去重pahole 内部链接 libbpf,调用 btf_dedup() 将原始 BTF(包含因头文件重复包含产生的大量重复类型)压缩到最终大小;
  4. 嵌入 vmlinux:去重后的 BTF 通过 objcopy --add-section .BTF=vmlinux.btf 嵌入 vmlinux ELF;
  5. 运行时访问:内核启动后将 vmlinux BTF 暴露到 /sys/kernel/btf/vmlinux

5.2 模块 BTF

每个内核模块也会生成自己的 BTF(如果 CONFIG_DEBUG_INFO_BTF_MODULES=y),存储在模块的 ko 文件中,加载时通过 /sys/kernel/btf/<模块名> 暴露。模块 BTF 的 type_id 会进行偏移调整——模块的第一个类型紧接 vmlinux BTF 的最后一个类型之后,形成统一的类型命名空间。

六、btf_dedup:BTF 去重算法

6.1 为什么需要去重

内核源码中的同一个类型可能出现在多个地方:

// include/linux/types.h
typedef unsigned int __u32;        // type_id=100

// include/linux/counter.h (includes types.h)
typedef unsigned int __u32;        // type_id=1500

// arch/x86/include/asm/page.h (includes types.h)
typedef unsigned int __u32;        // type_id=3000

在 DWARF 中,这三个 __u32 分别对应不同的编译单元(CU),pahole 在转换时会把每个 DWARF DIE 生成一个独立的 BTF type entry。如果不去重,三个完全相同的 __u32 typedef 会浪费类型空间和字符串表空间。

6.2 算法原理

btf_dedup()tools/lib/bpf/btf.c)基于结构等价性(structural equivalence)进行去重:

  1. 哈希阶段:为每个类型计算一个哈希值——对 INT 类型,哈希输入为 (name_off, size, encoding);对 STRUCT 类型,哈希输入为 (name_off, size, member_count, 每个成员的 (name_off, type_id, offset))。递归依赖的 type_id 在哈希时使用当前的临时映射。

  2. 等价性映射阶段:对每个哈希桶中的类型进行等价性比较。两个 STRUCT 类型被认为等价当且仅当:

    • 名称相同(或两者都为匿名)
    • 大小相同
    • 成员数量相同
    • 每个对应成员的名称、类型(经过映射后)、偏移量相同
  3. 重映射阶段:创建从旧 type_id 到新 type_id 的映射表,所有引用旧 type_id 的地方替换为新 type_id。

  4. 压缩阶段:移除重复的 type entry,重新编号,生成紧凑的 BTF。

6.3 去重的边界

去重不会合并名称不同但结构相同的类型。struct {int x; int y;} 如果分别以 pointvector 命名,去重后仍是两个独立类型——名称是结构等价性判断的输入之一。这是刻意的设计选择:合并不同名称的类型会导致调试工具(bpftool btf dump format c)输出混乱(类型名丢失)。

去重的一个副作用是消除 "forward declaration" 冲突——如果一个 FWD 类型指向一个后续定义的 STRUCT,去重会将 FWD 直接映射到 STRUCT 的 type_id,消除间接引用。

七、常用操作:通过 BTF 访问内核类型

7.1 从 /sys/kernel/btf/vmlinux 获取 BTF

# 查看 vmlinux BTF 的大小
ls -lh /sys/kernel/btf/vmlinux

# 导出为原始二进制
cp /sys/kernel/btf/vmlinux /tmp/vmlinux.btf

# 查看 BTF 中的所有类型
bpftool btf dump file /sys/kernel/btf/vmlinux | head -100

# 生成 vmlinux.h
bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h

7.2 查找特定类型

# 在 BTF 中查找 struct task_struct
bpftool btf dump file /sys/kernel/btf/vmlinux | grep -A 50 'STRUCT "task_struct"'

# 查找特定函数签名
bpftool btf dump file /sys/kernel/btf/vmlinux | grep -A 20 'FUNC_PROTO.*tcp_connect'

# 查看所有枚举
bpftool btf dump file /sys/kernel/btf/vmlinux | grep '^\[.*\] ENUM '

7.3 检查模块 BTF

# 列出所有可用的 BTF
ls /sys/kernel/btf/

# 查看特定模块的类型
bpftool btf dump file /sys/kernel/btf/nf_tables | head -50

八、小结

BTF 是 eBPF 基础设施的类型语言。从 struct btf_header 的固定 24 字节头,到 20 种 type kind 的变长编码,再到 BTF.ext 的 func_info/line_info 指令级映射——这条格式管线支撑了 verifier 的类型检查、bpftool 的人性化输出、CO-RE 的跨版本字段定位。内核构建流程中 pahole 的 DWARF-to-BTF 转换将 120 MB 的调试信息压缩到 1-2 MB,btf_dedup() 的结构等价性去重让这个压缩成为可能。下一篇文章进入 CO-RE 重定位引擎——BTF 的下游消费者,讲清 libbpf 如何使用 BTF 在加载时修补 BPF 指令来实现在不同内核版本上的可移植性。


参考

  1. Linux 内核源码 include/uapi/linux/btf.h(kernel 6.6):struct btf_headerstruct btf_type、BTF_KIND_* 枚举及其成员结构体
  2. Linux 内核源码 tools/lib/bpf/btf.cbtf_dedup() 去重算法实现
  3. Linux 内核源码 tools/lib/bpf/btf.h:BTF 解析宏(BTF_INFO_KIND()BTF_INFO_VLEN()
  4. Linux 内核文档 Documentation/bpf/btf.rst:BTF 格式规范
  5. Linux 内核文档 Documentation/bpf/bpf_devel_QA.rst:BTF 生成的编译要求
  6. pahole 源码(dwarves 项目):DWARF 到 BTF 的编码器实现

上一篇libbpf 加载器工程(第 10 篇)

下一篇CO-RE 重定位引擎(第 12 篇)

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2026-06-12 · kernel / ebpf

【eBPF 内核实现深度拆解】从验证器到 JIT,从 BTF 到调度器

eBPF 内核虚拟机内部实现系统讲解:BPF 指令集与寄存器机器、验证器的抽象解释与状态裁剪、JIT 编译器后端、Map 各类型的并发与内存模型、helper 函数注册与类型检查、BTF 格式规范与 CO-RE 重定位引擎、libbpf 加载器工程、fentry/fexit 蹦床机制、sched_ext 调度器内核接口。面向想读懂 eBPF 内核源码、写生产级 BPF 程序的系统工程师。

2026-06-12 · kernel / ebpf

【eBPF 内核实现深度拆解】实战:构建微型 eBPF 可观测 Agent

把 01--17 的知识串成一条实践线——从 libbpf skeleton 写第一个 BPF 程序、加载到内核、用 ring buffer 回传事件、用 CO-RE 实现跨内核版本兼容、map pinning 实现热升级、配上半自动化的 verifier 错误排障流程——构建一个麻雀虽小五脏俱全的 eBPF 可观测 Agent。


By .