eBPF-第二章-环境准备与安装部署
eBPF 第二章 环境准备与安装部署
版本:2026.07 | 作者:Pozicaiman | 日期:2026-07-24
关键词:eBPF环境、libbpf、BCC、bpftrace、CO-RE、内核配置
适用版本:Linux Kernel 4.18+ / 推荐 5.4+,libbpf 1.0+,BCC 0.28+
2.1 环境要求与前置条件
2.1.1 内核版本要求
eBPF 的能力与 Linux 内核版本强相关,不同版本支持的程序类型、Helper 函数、Map 类型均有差异。下表汇总了各内核版本的关键特性:
| 内核版本 | 发布年份 | 关键 eBPF 特性 | 推荐度 | 说明 |
|---|---|---|---|---|
| 4.0 - 4.3 | 2015 | eBPF 基础架构引入,支持 kprobe/tracepoint | 不推荐 | 功能极其有限,仅供了解 |
| 4.4 - 4.9 | 2016 | XDP 支持、perf_event 程序、BPF Map in Map | 最低可用 | 基础观测可用,无 CO-RE |
| 4.10 - 4.14 | 2017 | bpf 系统调用扩展、cgroup sock 程序、LRU Map | 可用 | BCC 开发可用 |
| 4.15 - 4.19 | 2018 | BTF 引入、bpf_iter、socket filter 增强 | 推荐(LTS) | 4.19 LTS 是长期支持底线 |
| 5.0 - 5.4 | 2019 | CO-RE(Compile Once-Run Everywhere)、bpf_link、BTF dedup | 强烈推荐(LTS) | CO-RE 成熟,libbpf 推荐 |
| 5.5 - 5.10 | 2020 | bpf_loop、ring buffer Map、sk_storage、LSM BPF | 强烈推荐(LTS) | 5.10 LTS 是生产首选 |
| 5.11 - 5.15 | 2021 | bpf_iter 增强迭代、bpf_timer、CAP_BPF | 强烈推荐(LTS) | 无 root 运行成为可能 |
| 5.16 - 5.19 | 2022 | bpf_kptr、动态指针、BPF 链接类型扩展 | 推荐 | 更多内核子系统可观测 |
| 6.0 - 6.6 | 2023 | BPF 验证器增强、bpf_arena、Uprobe 重构 | 最新推荐(LTS) | 6.6 LTS 支持最新特性 |
| 6.7+ | 2024+ | bpf_iter 增强结构体迭代、更多 Helper 函数 | 前沿 | 持续演进中 |
生产环境建议:优先选择 5.10 LTS 或 6.1 LTS 内核,既能获得 CO-RE 支持,又有长期安全更新保障。
2.1.2 内核配置选项检查
eBPF 需要内核在编译时开启相关配置选项。以下配置项必须启用(y 或 m):
| 配置选项 | 必需性 | 说明 |
|---|---|---|
CONFIG_BPF |
必需 | 启用 BPF 核心框架 |
CONFIG_BPF_SYSCALL |
必需 | 启用 bpf() 系统调用,用户空间加载 BPF 程序的入口 |
CONFIG_BPF_JIT |
必需 | 启用 BPF JIT 编译,将 BPF 字节码转为原生机器码 |
CONFIG_HAVE_EBPF_JIT |
必需 | 标记架构支持 eBPF JIT(x86_64/ARM64/RISC-V) |
CONFIG_BPF_JIT_ALWAYS_ON |
推荐 | 强制开启 JIT,防止字节码解释器被禁用 |
CONFIG_BPF_UNPRIV_DEFAULT_OFF |
推荐 | 默认禁止非特权用户使用 BPF(安全加固) |
CONFIG_DEBUG_INFO_BTF |
必需(CO-RE) | 编译内核 BTF 调试信息,CO-RE 运行时依赖 |
CONFIG_DEBUG_INFO_BTF_MODULES |
推荐 | 为内核模块生成 BTF,支持模块相关 eBPF 程序 |
CONFIG_BPF_LSM |
可选 | 启用 BPF LSM(Linux Security Module),安全策略场景 |
CONFIG_NET_CLS_BPF |
可选 | 流量分类 BPF,网络场景 |
CONFIG_NET_ACT_BPF |
可选 | 流量动作 BPF,网络场景 |
CONFIG_XDP_SOCKETS |
可选 | AF_XDP 高性能数据包处理 |
CONFIG_CGROUP_BPF |
推荐 | cgroup 级别 BPF 程序 |
CONFIG_LIRC_BPF |
可选 | 红外遥控 BPF |
2.1.3 硬件架构要求
| 架构 | eBPF 支持 | JIT 支持 | 状态 | 说明 |
|---|---|---|---|---|
| x86_64 | 完整 | 完整 | 生产首选 | 最成熟的 eBPF 架构 |
| ARM64 (aarch64) | 完整 | 完整 | 生产推荐 | 云原生主流架构,5.x 起完全成熟 |
| RISC-V (riscv64) | 完整 | 完整(5.18+) | 可用 | 新兴架构,5.18 起支持 JIT |
| s390x | 完整 | 完整 | 可用 | IBM 大型机 |
| PowerPC (ppc64le) | 部分 | 完整 | 有限 | 部分特性受限 |
| MIPS | 部分 | 部分 | 不推荐 | JIT 支持不完整 |
| ARM32 (armv7) | 有限 | 部分 | 不推荐 | 受限于寄存器数量,仅基础功能 |
2.1.4 操作系统发行版支持矩阵
| 发行版 | 最低版本 | 默认内核 | BTF 支持 | libbpf 包 | BCC 包 | 推荐度 |
|---|---|---|---|---|---|---|
| Ubuntu 20.04 LTS | 20.04 | 5.4/5.15 | 20.10+ | 是 | 是 | 推荐 |
| Ubuntu 22.04 LTS | 22.04 | 5.15/6.8 | 是 | 是 | 是 | 强烈推荐 |
| Ubuntu 24.04 LTS | 24.04 | 6.8 | 是 | 是 | 是 | 最新推荐 |
| RHEL 8.x | 8.0 | 4.18 | 8.2+ | 是 | 是 | 推荐(企业) |
| RHEL 9.x | 9.0 | 5.14 | 是 | 是 | 是 | 强烈推荐(企业) |
| CentOS Stream 8/9 | 8 | 4.18/5.14 | 8+ | 是 | 是 | 推荐 |
| Rocky Linux 8/9 | 8 | 4.18/5.14 | 是 | 是 | 是 | 推荐 |
| AlmaLinux 8/9 | 8 | 4.18/5.14 | 是 | 是 | 是 | 推荐 |
| Debian 11 | 11 | 5.10 | 是 | 是 | 是 | 推荐 |
| Debian 12 | 12 | 6.1 | 是 | 是 | 是 | 强烈推荐 |
| Fedora 36+ | 36 | 5.17+ | 是 | 是 | 是 | 前沿测试 |
| Alpine 3.16+ | 3.16 | 5.15+ | 3.18+ | 是(社区) | 否(需编译) | 容器场景 |
| openSUSE Tumbleweed | - | 6.x | 是 | 是 | 是 | 可用 |
2.1.5 eBPF 开发环境组件关系
上图展示了 eBPF 开发环境的完整组件链路:从源码编译(Clang/LLVM)到 BPF 字节码生成,通过 libbpf 加载器(CO-RE Skeleton 机制)调用 bpf() 系统调用,经验证器安全检查后由 JIT 编译为原生代码挂载到内核 Hook 点。
2.2 工具链安装与配置
2.2.1 LLVM/Clang 编译器安装
LLVM/Clang 是 eBPF 程序的编译器前端,将 C 语言源码编译为 BPF 字节码。Clang 版本直接影响 eBPF 程序的编译能力。
方式一:包管理器安装(推荐快速使用)
# ===== Ubuntu / Debian =====
sudo apt-get update
sudo apt-get install -y clang llvm lld lldb
# 指定版本安装(推荐 clang-15+)
sudo apt-get install -y clang-15 llvm-15 lld-15
# 验证安装
clang --version
llvm-strip --version
# ===== RHEL / CentOS / Rocky / AlmaLinux =====
sudo dnf install -y clang llvm lld
# 或指定模块版本
sudo dnf module install llvm-toolset
# ===== Fedora =====
sudo dnf install -y clang llvm lld
# ===== Alpine =====
sudo apk add clang llvm lld
方式二:源码编译安装(需要特定版本时)
# 安装编译依赖
sudo apt-get install -y build-essential cmake ninja-build python3 git
# 下载 LLVM 源码(以 17.0.6 为例)
git clone --depth 1 --branch llvmorg-17.0.6 https://github.com/llvm/llvm-project.git
cd llvm-project
# 创建构建目录
mkdir -p build && cd build
# 配置 CMake(仅编译 Clang 和 BPF 后端,大幅减少编译时间)
cmake -G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=/usr/local/llvm-17 \
-DLLVM_ENABLE_PROJECTS="clang" \
-DLLVM_TARGETS_TO_BUILD="BPF;X86" \
-DLLVM_ENABLE_RTTI=ON \
-DLLVM_ENABLE_EH=ON \
-DLLVM_BUILD_TESTS=OFF \
-DLLVM_BUILD_EXAMPLES=OFF \
../llvm
# 编译并安装(根据 CPU 核数调整 -j 参数)
ninja -j$(nproc)
sudo ninja install
# 配置环境变量
echo 'export PATH=/usr/local/llvm-17/bin:$PATH' >> ~/.bashrc
echo 'export LD_LIBRARY_PATH=/usr/local/llvm-17/lib:$LD_LIBRARY_PATH' >> ~/.bashrc
source ~/.bashrc
# 验证
clang --version
版本选择建议
| Clang 版本 | BPF 目标支持 | CO-RE 支持 | 推荐度 | 说明 |
|---|---|---|---|---|
| < 7.0 | 部分 | 否 | 不推荐 | BPF 后端不完善 |
| 8.0 - 9.0 | 是 | 部分 | 可用 | 早期 CO-RE 支持 |
| 10.0 - 12.0 | 是 | 是 | 推荐 | BPF 代码生成稳定 |
| 13.0 - 15.0 | 是 | 是 | 强烈推荐 | 支持最新 BPF 特性 |
| 16.0+ | 是 | 是 | 最新推荐 | 完整支持 BPF 语法 |
兼容性说明:Clang 版本与内核版本无强绑定关系,但较新的 Clang 能更好地编译使用新 BPF 特性的程序。建议 Clang >= 12。
2.2.2 libbpf 安装
libbpf 是 Linux 内核官方维护的 BPF 程序加载库,是 CO-RE(Compile Once - Run Everywhere) 的核心组件。
方式一:源码编译安装(推荐,获取最新版本)
# 安装依赖
sudo apt-get install -y pkg-config libelf-dev libzstd-dev
# 克隆 libbpf 仓库
git clone https://github.com/libbpf/libbpf.git
cd libbpf
# 切换到稳定分支(推荐 1.3.0+)
git checkout v1.3.0
# 方式 A:使用 meson 构建(libbpf 1.0+ 推荐)
sudo apt-get install -y meson ninja-build
# 配置并编译
meson setup build -Dprefix=/usr -Dlibdir=lib
ninja -C build
sudo ninja -C build install
# 方式 B:使用传统 Makefile 构建
cd src
make -j$(nproc)
sudo make install
sudo ldconfig
# 验证安装
pkg-config --modversion libbpf
# 预期输出: 1.3.0
方式二:包管理器安装
# Ubuntu / Debian
sudo apt-get install -y libbpf-dev libbpf1
# RHEL / CentOS / Rocky
sudo dnf install -y libbpf-devel libbpf
# Alpine
sudo apk add libbpf-dev
# 验证
pkg-config --cflags --libs libbpf
# 预期输出: -I/usr/include -lbpf
libbpf 与内核版本兼容性
| libbpf 版本 | 最低内核要求 | 关键特性 | 说明 |
|---|---|---|---|
| 0.x (0.8之前) | 4.15+ | 基础 BPF 加载 | 旧 API,不推荐 |
| 1.0 | 4.18+ | API 稳定,CO-RE 成熟 | 首个稳定版本 |
| 1.1 - 1.3 | 5.4+ | bpf_link 增强,ringbuf | 推荐生产使用 |
| 1.4+ | 5.10+ | 最新特性支持 | 前沿使用 |
关键原则:libbpf 向后兼容旧内核。使用 libbpf 1.x 可以加载到 4.18+ 的内核上运行(前提是程序未使用新内核独有的特性)。libbpf 会在运行时探测内核能力并优雅降级。
2.2.3 BCC 工具集安装
BCC (BPF Compiler Collection) 是基于 Python/C++ 的高层 eBPF 开发框架,提供丰富的开箱即用工具和 Python 绑定。
BCC 完整安装流程
# ===== Ubuntu 22.04 / 24.04 =====
# 1. 安装构建依赖
sudo apt-get install -y \
bpfcc-tools linux-headers-$(uname -r) \
build-essential clang llvm \
libelf-dev libbpf-dev \
python3 python3-dev python3-pip \
cmake git flex bison
# 2. 克隆 BCC 仓库
git clone --recursive https://github.com/iovisor/bcc.git
cd bcc
# 3. 创建构建目录
mkdir -p build && cd build
# 4. CMake 配置
cmake -DCMAKE_INSTALL_PREFIX=/usr \
-DPYTHON_CMD=python3 \
..
# 5. 编译(耗时约 5-10 分钟)
make -j$(nproc)
# 6. 安装
sudo make install
# 7. 安装 Python 绑定
sudo pip3 install bcc
# 或从源码安装
cd ../src/python
sudo python3 setup.py install
# 8. 验证安装
sudo python3 -c "from bcc import BPF; print('BCC installed successfully')"
# 9. 运行自带工具验证
sudo /usr/share/bcc/tools/execsnoop
# 预期:输出系统进程执行事件
# ===== RHEL 9 / Rocky 9 / AlmaLinux 9 =====
sudo dnf install -y \
bcc bcc-tools \
python3-bcc \
kernel-devel-$(uname -r) \
clang llvm \
elfutils-libelf-devel libbpf-devel
# 验证
sudo /usr/share/bcc/tools/execsnoop
# ===== Alpine Linux =====
# BCC 在 Alpine 上需要源码编译,依赖较多
sudo apk add --no-cache \
build-base clang llvm-dev elfutils-dev \
libbpf-dev python3-dev cmake git flex-dev bison
git clone https://github.com/iovisor/bcc.git
cd bcc && mkdir build && cd build
cmake .. && make -j$(nproc) && sudo make install
BCC Python 绑定验证
#!/usr/bin/env python3
"""BCC Python 绑定验证脚本"""
from bcc import BPF
# 简单的 BPF 程序
bpf_text = """
#include <uapi/linux/ptrace.h>
BPF_HASH(counts, u32, u64);
int hello(void *ctx) {
u32 pid = bpf_get_current_pid_tgid() >> 32;
u64 one = 1;
u64 *existing = counts.lookup(&pid);
if (existing) {
(*existing)++;
} else {
counts.update(&pid, &one);
}
return 0;
}
"""
bpf = BPF(text=bpf_text)
print("BCC version check passed. BPF object created successfully.")
print("Available BPF functions: " + str(list(bpf.functions.keys())))
常见安装问题排查
# 问题1:找不到内核头文件
# 解决:安装对应内核版本头文件
sudo apt-get install -y linux-headers-$(uname -r)
# 问题2:Python 导入错误 ModuleNotFoundError: No module named 'bcc'
# 解决:确认 Python 版本一致,重新安装绑定
python3 -c "import sys; print(sys.path)"
sudo python3 -m pip install --force-reinstall bcc
# 问题3:Clang 编译 BPF 程序报错 "target triple invalid"
# 解决:确认 Clang 版本 >= 7,且支持 BPF 后端
echo 'int main() { return 0; }' | clang -target bpf -c -x c - -o /dev/null && echo "BPF target OK"
# 问题4:运行时报错 "bpf: Operation not permitted"
# 解决:确认以 root 运行或具有 CAP_BPF/CAP_SYS_ADMIN 能力
sudo cat /proc/sys/kernel/unprivileged_bpf_disabled
# 若为 1 或 2,非特权用户无法运行 BPF
2.2.4 bpftrace 安装
bpftrace 是基于 eBPF 的高级追踪语言,提供类似 awk/DTrace 的一行命令和脚本能力,是快速排障利器。
方式一:包管理器安装(最快)
# Ubuntu 22.04+
sudo apt-get install -y bpftrace
# RHEL 9 / Rocky 9
sudo dnf install -y bpftrace
# Fedora
sudo dnf install -y bpftrace
# Debian 12
sudo apt-get install -y bpftrace
# 验证
bpftrace --version
# 预期: bpftrace v0.x.x
方式二:源码编译安装
# 安装依赖
sudo apt-get install -y \
build-essential cmake ninja-build \
clang llvm-dev libelf-dev libbpf-dev \
libbcc-dev bison flex \
libprotobuf-dev protobuf-compiler \
libgrpc++-dev libgrpc-dev \
asciidoctor
# 克隆仓库
git clone https://github.com/bpftrace/bpftrace.git
cd bpftrace
git submodule update --init --recursive
# 构建
mkdir -p build && cd build
cmake -G Ninja -DCMAKE_BUILD_TYPE=Release ..
ninja -j$(nproc)
# 安装
sudo ninja install
# 验证
bpftrace --version
方式三:静态二进制(免安装)
# 下载官方静态编译二进制(无需任何依赖)
wget https://github.com/bpftrace/bpftrace/releases/latest/download/bpftrace-static-amd64.tar.gz
tar xzf bpftrace-static-amd64.tar.gz
sudo cp bpftrace /usr/local/bin/
# 验证
bpftrace --version
bpftrace 语言基础语法
// bpftrace 语法结构
// 探针类型:标识符[/过滤器/] { 动作语句 }
// 1. BEGIN 探针 - 程序启动时执行
BEGIN { printf("Starting bpftrace...\n"); }
// 2. END 探针 - 程序结束时执行
END { printf("Done. Exiting.\n"); }
// 3. kprobe 探针 - 内核函数入口
kprobe:do_sys_open { printf("open() called by %s\n", comm); }
// 4. kretprobe 探针 - 内核函数返回
kretprobe:do_sys_open /retval < 0/ { printf("open() failed: %d\n", retval); }
// 5. tracepoint 探针 - 内核静态追踪点
tracepoint:syscalls:sys_enter_openat { @opens = count(); }
// 6. interval 探针 - 定时器
interval:s:1 { print(@opens); clear(@opens); }
// 7. uprobe 探针 - 用户空间函数
uprobe:/bin/bash:readline { printf("bash readline: %s\n", str(arg0)); }
// 内置变量说明
// pid - 进程 ID
// tid - 线程 ID
// uid - 用户 ID
// comm - 进程名
// kstack - 内核调用栈
// ustack - 用户调用栈
// arg0-9 - 函数参数
// retval - 返回值(仅 kretprobe/uprobe)
// 内置函数说明
// count() - 计数
// sum() - 求和
// avg() - 平均值
// hist() - 直方图(2的幂次)
// lhist() - 线性直方图
// printf() - 格式化输出
// str() - 读取字符串指针
// delete() - 删除 Map 元素
// clear() - 清空 Map
// exit() - 退出 bpftrace
2.2.5 bpftool 安装
bpftool 是内核官方提供的 BPF 管理工具,用于查看、加载、卸载和调试 BPF 程序与 Maps。
# ===== Ubuntu / Debian =====
sudo apt-get install -y linux-tools-common linux-tools-$(uname -r)
# 或安装通用包
sudo apt-get install -y bpftool
# ===== RHEL / CentOS / Rocky / AlmaLinux =====
sudo dnf install -y bpftool
# ===== Fedora =====
sudo dnf install -y bpftool
# ===== 源码编译(从内核源码树) =====
git clone --depth 1 https://github.com/torvalds/linux.git
cd linux/tools/bpf/bpftool
make -j$(nproc)
sudo make install
# 验证安装
bpftool version
# 预期输出: v7.x.x
# 功能验证
bpftool prog show # 列出所有已加载的 BPF 程序
bpftool map show # 列出所有 BPF Maps
bpftool feature probe # 探测内核 BPF 特性支持
bpftool btf dump file /sys/kernel/btf/vmlinux format c | head -20 # 查看 BTF 信息
2.3 内核配置验证
2.3.1 检查内核 BPF 特性
#!/bin/bash
# ===== 内核 BPF 配置验证命令 =====
# 1. 检查内核版本
echo "=== 内核版本 ==="
uname -r
# 预期: 5.10+ 推荐版本
# 2. 检查关键内核配置选项
echo -e "\n=== 内核 BPF 配置选项 ==="
for config in CONFIG_BPF CONFIG_BPF_SYSCALL CONFIG_BPF_JIT \
CONFIG_HAVE_EBPF_JIT CONFIG_BPF_JIT_ALWAYS_ON \
CONFIG_DEBUG_INFO_BTF CONFIG_BPF_UNPRIV_DEFAULT_OFF \
CONFIG_CGROUP_BPF CONFIG_BPF_LSM; do
val=$(grep -E "^${config}[= ]" /boot/config-$(uname -r) 2>/dev/null \
|| zcat /proc/config.gz 2>/dev/null | grep -E "^${config}[= ]")
if [ -n "$val" ]; then
echo " $val"
else
echo " $config=未找到(可能未启用)"
fi
done
# 3. 检查 BTF 是否可用(CO-RE 必需)
echo -e "\n=== BTF 调试信息检查 ==="
ls -lh /sys/kernel/btf/vmlinux && echo "BTF: 可用 (CO-RE 支持)" \
|| echo "BTF: 不可用 (CO-RE 不可用)"
# 4. 检查 bpf() 系统调用是否可用
echo -e "\n=== bpf 系统调用 ==="
grep " T bpf$" /proc/kallsyms | head -5 && echo "bpf syscall: 可用" \
|| echo "bpf syscall: 不可用"
2.3.2 检查 BPF JIT 状态
# 检查 BPF JIT 是否启用(1=启用, 0=禁用)
cat /proc/sys/net/core/bpf_jit_enable
# 输出 1 表示 JIT 已启用
# 检查 BPF JIT 硬件卸载(需要网卡支持)
cat /proc/sys/net/core/bpf_jit_harden
# 0=禁用加固, 1=非特权用户加固, 2=完全加固
# 检查 BPF JIT kallsyms 暴露
cat /proc/sys/net/core/bpf_jit_kallsyms
# 1=JIT 代码符号暴露给 /proc/kallsyms (调试用)
# 启用 JIT(如未启用)
sudo sysctl -w net.core.bpf_jit_enable=1
# 永久生效
echo 'net.core.bpf_jit_enable=1' | sudo tee -a /etc/sysctl.d/99-bpf.conf
2.3.3 检查 BPF Map 大小限制
# 检查 BPF Map 相关限制
echo "=== BPF Map 限制 ==="
# 最大 Map 数量
echo "最大 Map 数量: $(cat /proc/sys/kernel/bpf_map_max_entries 2>/dev/null || echo 'N/A')"
# 单个 Map 最大内存(字节)
echo "单个 Map 最大内存: $(cat /proc/sys/kernel/bpf_map_max_size 2>/dev/null || echo 'N/A')"
# 使用 bpftool 查看当前 Maps
echo -e "\n当前已加载 BPF Maps:"
sudo bpftool map show
2.3.4 检查 RLIMIT_MEMLOCK 设置
BPF 程序加载时需要锁定内存,受 RLIMIT_MEMLOCK 限制。内核 5.11+ 引入了 CAP_BPF,不再依赖 memlock 限制。
# 检查当前 memlock 限制(单位 KB)
ulimit -l
# 输出 8192 表示 8MB,可能不够用
# 临时调整为无限制
ulimit -l unlimited
# 永久调整(编辑 /etc/security/limits.conf)
sudo tee -a /etc/security/limits.conf << 'EOF'
* soft memlock unlimited
* hard memlock unlimited
root soft memlock unlimited
root hard memlock unlimited
EOF
# 对于 systemd 服务,需要编辑服务单元的 LimitMEMLOCK
sudo sed -i 's/#DefaultLimitMEMLOCK=/DefaultLimitMEMLOCK=infinity/' /etc/systemd/system.conf
# 内核 5.11+ 检查 CAP_BPF 能力
echo -e "\n=== 内核版本与 CAP_BPF 支持 ==="
KV=$(uname -r | awk -F. '{print $1*100+$2}')
if [ "$KV" -ge 511 ]; then
echo "内核 $(uname -r): 支持 CAP_BPF (5.11+)"
echo "使用 CAP_BPF 时不需要调整 memlock 限制"
else
echo "内核 $(uname -r): 不支持 CAP_BPF,需调整 memlock 限制"
fi
2.3.5 系统参数调优建议
#!/bin/bash
# ===== eBPF 系统参数调优脚本 =====
# 运行: sudo bash bpf-sysctl-tune.sh
set -euo pipefail
CONF_FILE="/etc/sysctl.d/99-ebpf.conf"
echo "=== eBPF 系统参数调优 ==="
# 定义调优参数
PARAMS=(
"net.core.bpf_jit_enable=1" # 启用 BPF JIT
"net.core.bpf_jit_harden=1" # 非特权用户加固(安全)
"net.core.bpf_jit_kallsyms=0" # 生产环境关闭符号暴露
"kernel.unprivileged_bpf_disabled=1" # 禁止非特权用户使用 BPF
"kernel.perf_event_paranoid=1" # 允许观测性能事件
"kernel.kptr_restrict=1" # 限制内核指针暴露
)
# 写入配置文件
echo "# eBPF 系统参数配置 - by Pozicaiman" | sudo tee "$CONF_FILE" > /dev/null
for param in "${PARAMS[@]}"; do
key="${param%%=*}"
value="${param##*=}"
echo "$key = $value" | sudo tee -a "$CONF_FILE" > /dev/null
sudo sysctl -w "$key=$value" 2>/dev/null || echo " 警告: $key 设置失败(可能内核版本不支持)"
echo "已设置: $key = $value"
done
echo -e "\n配置已写入 $CONF_FILE"
echo "永久生效需重启或执行: sudo sysctl --system"
2.3.6 完整的内核配置检查脚本
#!/bin/bash
# ===== eBPF 内核环境完整检查脚本 =====
# 运行: sudo bash check-ebpf-kernel.sh
# Author: Pozicaiman
# Date: 2026-07-24
set -euo pipefail
GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[1;33m'
NC='\033[0m'
pass() { echo -e "${GREEN}[PASS]${NC} $1"; }
fail() { echo -e "${RED}[FAIL]${NC} $1"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
echo "==============================="
echo " eBPF 内核环境检查"
echo "==============================="
echo "日期: $(date '+%Y-%m-%d %H:%M:%S')"
echo ""
# --- 内核版本 ---
echo "--- 1. 内核版本 ---"
KV=$(uname -r)
KV_MAJOR=$(echo "$KV" | cut -d. -f1)
KV_MINOR=$(echo "$KV" | cut -d. -f2)
KV_NUM=$((KV_MAJOR * 100 + KV_MINOR))
echo "当前内核: $KV"
if [ "$KV_NUM" -ge 510 ]; then
pass "内核版本 >= 5.10 (推荐版本)"
elif [ "$KV_NUM" -ge 504 ]; then
warn "内核版本 5.x,建议升级到 5.10+"
elif [ "$KV_NUM" -ge 415 ]; then
warn "内核版本 4.15+,基本可用,但缺少部分新特性"
else
fail "内核版本过低 (<4.15),eBPF 支持有限"
fi
# --- 配置文件路径 ---
echo -e "\n--- 2. 内核配置检查 ---"
CONFIG_FILE="/boot/config-$(uname -r)"
if [ ! -f "$CONFIG_FILE" ]; then
if [ -f "/proc/config.gz" ]; then
CONFIG_SRC="zcat /proc/config.gz"
else
CONFIG_SRC=""
warn "未找到内核配置文件,无法检查配置选项"
fi
else
CONFIG_SRC="cat $CONFIG_FILE"
fi
check_config() {
local config=$1 required=$2
if [ -z "$CONFIG_SRC" ]; then return; fi
val=$(eval "$CONFIG_SRC" 2>/dev/null | grep "^${config}=" | cut -d= -f2)
if [ "$val" = "y" ] || [ "$val" = "m" ]; then
pass "$config=$val"
else
if [ "$required" = "required" ]; then
fail "$config 未启用 (必需)"
else
warn "$config 未启用 (可选)"
fi
fi
}
check_config "CONFIG_BPF" "required"
check_config "CONFIG_BPF_SYSCALL" "required"
check_config "CONFIG_BPF_JIT" "required"
check_config "CONFIG_HAVE_EBPF_JIT" "required"
check_config "CONFIG_BPF_JIT_ALWAYS_ON" "optional"
check_config "CONFIG_DEBUG_INFO_BTF" "required"
check_config "CONFIG_DEBUG_INFO_BTF_MODULES" "optional"
check_config "CONFIG_BPF_UNPRIV_DEFAULT_OFF" "optional"
check_config "CONFIG_CGROUP_BPF" "optional"
check_config "CONFIG_BPF_LSM" "optional"
check_config "CONFIG_NET_CLS_BPF" "optional"
check_config "CONFIG_XDP_SOCKETS" "optional"
# --- BTF 检查 ---
echo -e "\n--- 3. BTF 调试信息 (CO-RE 依赖) ---"
if [ -f "/sys/kernel/btf/vmlinux" ]; then
BTF_SIZE=$(stat -c%s /sys/kernel/btf/vmlinux 2>/dev/null || stat -f%z /sys/kernel/btf/vmlinux)
pass "BTF 可用 (vmlinux BTF: $BTF_SIZE bytes)"
else
fail "BTF 不可用 - CO-RE 程序将无法运行"
echo " 解决方案:"
echo " 1. 升级到 CONFIG_DEBUG_INFO_BTF=y 的内核"
echo " 2. 或安装 pahole 工具重新编译内核"
fi
# --- JIT 状态 ---
echo -e "\n--- 4. BPF JIT 状态 ---"
JIT=$(cat /proc/sys/net/core/bpf_jit_enable 2>/dev/null || echo "N/A")
if [ "$JIT" = "1" ]; then
pass "BPF JIT 已启用"
else
fail "BPF JIT 未启用 (当前: $JIT)"
echo " 解决: sudo sysctl -w net.core.bpf_jit_enable=1"
fi
# --- memlock 限制 ---
echo -e "\n--- 5. 内存锁定限制 ---"
MEMLOCK=$(ulimit -l)
if [ "$MEMLOCK" = "unlimited" ] || [ "$MEMLOCK" -ge 1048576 ]; then
pass "memlock 限制充足 ($MEMLOCK KB)"
else
warn "memlock 限制较低 ($MEMLOCK KB),可能影响 BPF 程序加载"
echo " 解决: ulimit -l unlimited 或配置 CAP_BPF"
fi
# --- 工具链检查 ---
echo -e "\n--- 6. 工具链检查 ---"
check_tool() {
if command -v "$1" &> /dev/null; then
ver=$("$1" --version 2>&1 | head -1)
pass "$1: $ver"
else
fail "$1: 未安装"
fi
}
check_tool clang
check_tool llvm-strip
check_tool bpftool
check_tool bpftrace
# libbpf 检查
if pkg-config --exists libbpf 2>/dev/null; then
pass "libbpf: $(pkg-config --modversion libbpf)"
else
fail "libbpf: 未安装 (pkg-config 未找到)"
fi
echo -e "\n==============================="
echo " 检查完成"
echo "==============================="
2.4 开发环境搭建
2.4.1 VS Code eBPF 开发环境配置
推荐插件列表
| 插件名称 | 功能 | 必要性 |
|---|---|---|
| ms-vscode.cpptools | C/C++ 智能提示、调试 | 必需 |
| llvm-vs-code-extensions.vscode-clangd | Clangd 代码分析(替代 C/C++ 插件) | 推荐 |
| BPF ( Marketplace 搜索 ) | BPF 语法高亮、代码片段 | 推荐 |
| vadimcn.vscode-lldb | LLDB 调试器集成 | 推荐 |
| ms-python.python | Python 开发(BCC 用) | BCC 必需 |
| xaver.clang-format | 代码格式化 | 可选 |
| GitHub.copilot | AI 辅助编码 | 可选 |
VS Code 配置(.vscode/settings.json)
{
"C_Cpp.default.includePath": [
"${workspaceFolder}/**",
"/usr/include/**",
"/usr/include/x86_64-linux-gnu/**",
"${workspaceFolder}/vmlinux.h"
],
"C_Cpp.default.defines": [
"__BPF_TRACING__",
"__TARGET_ARCH_x86"
],
"clangd.arguments": [
"--clang-tidy",
"--header-insertion=iwyu",
"--completion-style=detailed"
],
"files.associations": {
"*.bpf.c": "c",
"*.h": "c",
"vmlinux.h": "c"
},
"[c]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "xaver.clang-format"
}
}
调试配置(.vscode/launch.json)
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug eBPF Loader (LLDB)",
"type": "lldb",
"request": "launch",
"program": "${workspaceFolder}/build/ebpf-loader",
"args": [],
"cwd": "${workspaceFolder}",
"env": {
"RUST_LOG": "debug"
},
"preLaunchTask": "build"
},
{
"name": "Debug BCC Python",
"type": "python",
"request": "launch",
"program": "${file}",
"console": "integratedTerminal",
"sudo": true
}
]
}
构建任务(.vscode/tasks.json)
{
"version": "2.0.0",
"tasks": [
{
"label": "build",
"type": "shell",
"command": "make",
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": ["$gcc"]
},
{
"label": "clean",
"type": "shell",
"command": "make clean"
},
{
"label": "generate vmlinux.h",
"type": "shell",
"command": "bpftool btf dump file /sys/kernel/btf/vmlinux format c > ${workspaceFolder}/vmlinux.h",
"problemMatcher": []
}
]
}
2.4.2 Docker 容器化开发环境
Dockerfile 示例
# ===== eBPF 开发环境 Dockerfile =====
# 构建: docker build -t ebpf-dev:latest .
# 运行: docker run --rm -it --privileged -v $(pwd):/src ebpf-dev:latest
FROM ubuntu:22.04
LABEL maintainer="Pozicaiman"
LABEL description="eBPF Development Environment"
ENV DEBIAN_FRONTEND=noninteractive
ENV LLVM_VERSION=15
# 安装系统依赖和开发工具链
RUN apt-get update && apt-get install -y \
build-essential cmake ninja-build git curl wget vim \
pkg-config flex bison \
clang-${LLVM_VERSION} llvm-${LLVM_VERSION} lld-${LLVM_VERSION} \
llvm-${LLVM_VERSION}-dev libclang-${LLVM_VERSION}-dev \
libelf-dev libzstd-dev \
python3 python3-dev python3-pip \
bison flex \
gdb strace ltrace \
iproute2 iputils-ping tcpdump net-tools \
linux-tools-common kmod \
&& rm -rf /var/lib/apt/lists/*
# 创建 clang 符号链接
RUN ln -sf /usr/bin/clang-${LLVM_VERSION} /usr/bin/clang \
&& ln -sf /usr/bin/llvm-strip-${LLVM_VERSION} /usr/bin/llvm-strip \
&& ln -sf /usr/bin/llc-${LLVM_VERSION} /usr/bin/llc
# 编译安装 libbpf (1.3.0)
RUN git clone --depth 1 --branch v1.3.0 https://github.com/libbpf/libbpf.git /tmp/libbpf \
&& cd /tmp/libbpf/src \
&& make -j$(nproc) \
&& make install \
&& ldconfig \
&& rm -rf /tmp/libbpf
# 编译安装 bpftool
RUN git clone --depth 1 https://github.com/libbpf/bpftool.git /tmp/bpftool \
&& cd /tmp/bpftool/src \
&& make -j$(nproc) \
&& make install \
&& rm -rf /tmp/bpftool
# 安装 bpftrace(静态二进制)
RUN wget -qO /tmp/bpftrace.tar.gz \
https://github.com/bpftrace/bpftrace/releases/latest/download/bpftrace-static-amd64.tar.gz \
&& tar xzf /tmp/bpftrace.tar.gz -C /usr/local/bin/ \
&& chmod +x /usr/local/bin/bpftrace \
&& rm /tmp/bpftrace.tar.gz
# 安装 BCC Python 绑定
RUN pip3 install bcc
# 设置工作目录
WORKDIR /src
# 默认命令
CMD ["/bin/bash"]
docker-compose 配置
# ===== docker-compose.yml =====
# 启动: docker compose run --rm ebpf-dev
version: '3.8'
services:
ebpf-dev:
build:
context: .
dockerfile: Dockerfile
image: ebpf-dev:latest
container_name: ebpf-dev
privileged: true
volumes:
- ./src:/src
- /sys/kernel/btf:/sys/kernel/btf:ro
- /sys/kernel/tracing:/sys/kernel/tracing
- /sys/kernel/debug:/sys/kernel/debug
- /proc:/proc:rw
network_mode: host
mem_limit: 4g
cap_add:
- SYS_ADMIN
- SYS_PTRACE
- NET_ADMIN
- BPF
security_opt:
- apparmor:unconfined
environment:
- KERNEL_VERSION=host
容器化开发环境拓扑
2.4.3 Kubernetes 环境部署
eBPF 在 K8s 中的运行要求
在 Kubernetes 中运行 eBPF 程序需要注意以下要求:
- 特权模式:eBPF 加载需要
CAP_BPF或CAP_SYS_ADMIN - 文件系统挂载:需要访问
/sys/fs/bpf、/sys/kernel/tracing、/sys/kernel/debug - 主机网络:网络观测类程序通常需要
hostNetwork: true - 内核版本一致性:DaemonSet 模式下每个节点的内核版本应一致(或使用 CO-RE)
Pod Security 配置(DaemonSet 部署模式)
# ===== eBPF DaemonSet 部署清单 =====
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: ebpf-agent
namespace: kube-system
labels:
app: ebpf-agent
spec:
selector:
matchLabels:
app: ebpf-agent
template:
metadata:
labels:
app: ebpf-agent
spec:
hostNetwork: true
hostPID: true
hostIPC: false
securityContext:
privileged: true
runAsUser: 0
runAsGroup: 0
seccompProfile:
type: Unconfined
tolerations:
- operator: Exists
containers:
- name: ebpf-agent
image: ebpf-agent:latest
imagePullPolicy: IfNotPresent
securityContext:
privileged: true
capabilities:
add:
- SYS_ADMIN
- SYS_PTRACE
- NET_ADMIN
- BPF
- PERFMON
- SYS_RESOURCE
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
volumeMounts:
- name: bpf-fs
mountPath: /sys/fs/bpf
- name: tracing-fs
mountPath: /sys/kernel/tracing
- name: debug-fs
mountPath: /sys/kernel/debug
- name: btf
mountPath: /sys/kernel/btf
readOnly: true
- name: lib-modules
mountPath: /lib/modules
readOnly: true
- name: proc
mountPath: /proc
volumes:
- name: bpf-fs
hostPath:
path: /sys/fs/bpf
type: DirectoryOrCreate
- name: tracing-fs
hostPath:
path: /sys/kernel/tracing
type: Directory
- name: debug-fs
hostPath:
path: /sys/kernel/debug
type: Directory
- name: btf
hostPath:
path: /sys/kernel/btf
type: Directory
- name: lib-modules
hostPath:
path: /lib/modules
type: Directory
- name: proc
hostPath:
path: /proc
type: Directory
nodeSelector:
kubernetes.io/os: linux
K8s 中 eBPF 部署架构
上图展示了 Kubernetes 中 eBPF Agent 的 DaemonSet 部署模式:每个节点运行一个特权 Pod,通过 CO-RE 机制读取节点本地的 BTF 信息进行重定位,加载 BPF 程序到各自节点的内核中。不同节点可以运行不同内核版本,CO-RE 保证了 BPF 程序的跨版本兼容性。
2.5 第一个 eBPF 程序
2.5.1 使用 BCC 编写 Hello World
BCC 提供了最简单的 eBPF 开发方式,通过 Python 定义和加载 BPF C 代码。
完整 Python+BCC 代码示例
#!/usr/bin/env python3
"""
第一个 eBPF 程序 - BCC Hello World
功能:追踪系统中的 execve() 系统调用,打印新进程信息
运行:sudo python3 hello_bcc.py
"""
from bcc import BPF
import time
# BPF C 程序代码
bpf_program = """
#include <uapi/linux/ptrace.h>
#include <linux/sched.h>
// 定义事件数据结构
struct event_t {
u32 pid;
u32 ppid;
u64 timestamp;
char comm[16];
char filename[256];
};
// 定义 perf 事件 Map(用于内核到用户空间通信)
BPF_PERF_OUTPUT(events);
// 挂载到 execve 系统调用入口
int trace_execve(struct pt_regs *ctx,
const char __user *filename,
const char __user *const __user *__argv,
const char __user *const __user *__envp)
{
struct event_t event = {};
// 获取进程信息
event.pid = bpf_get_current_pid_tgid() >> 32;
event.ppid = bpf_get_current_task()->real_parent->tgid;
event.timestamp = bpf_ktime_get_ns();
bpf_get_current_comm(&event.comm, sizeof(event.comm));
// 从用户空间安全读取文件名
bpf_probe_read_user_str(&event.filename, sizeof(event.filename), filename);
// 提交事件到 perf buffer
events.perf_submit(ctx, &event, sizeof(event));
return 0;
}
"""
def format_event(cpu, data, size):
"""处理来自内核的事件"""
import ctypes as ct
class Event(ct.Structure):
_fields_ = [
("pid", ct.c_uint32),
("ppid", ct.c_uint32),
("timestamp", ct.c_uint64),
("comm", ct.c_char * 16),
("filename", ct.c_char * 256),
]
event = ct.cast(data, ct.POINTER(Event)).contents
ts = time.strftime("%H:%M:%S", time.localtime())
print(f"[{ts}] PID={event.pid:6d} PPID={event.ppid:6d} "
f"COMM={event.comm.decode():16s} FILE={event.filename.decode()}")
def main():
print("eBPF Hello World - 追踪进程创建 (execve)")
print("=" * 80)
print(f"{'时间':>10s} {'PID':>8s} {'PPID':>8s} {'进程名':>18s} {'可执行文件'}")
print("-" * 80)
# 编译并加载 BPF 程序
bpf = BPF(text=bpf_program)
# 挂载到 execve 系统调用
bpf.attach_kprobe(event="do_sys_openat2", fn_name="trace_execve")
# 注册 perf 事件回调
bpf["events"].open_perf_buffer(format_event)
print("\n等待进程创建事件... (Ctrl+C 退出)\n")
# 事件循环
try:
while True:
bpf.perf_buffer_poll(timeout=100)
except KeyboardInterrupt:
print("\n\n退出追踪。")
if __name__ == "__main__":
main()
运行和验证步骤
# 1. 确认 BCC 已安装
python3 -c "from bcc import BPF; print('BCC OK')"
# 2. 运行程序
sudo python3 hello_bcc.py
# 3. 在另一个终端触发 execve(执行命令)
ls
echo "hello"
cat /etc/hostname
# 4. 预期输出
# [14:30:01] PID= 12345 PPID= 12344 COMM=ls FILE=/bin/ls
# [14:30:02] PID= 12346 PPID= 12344 COMM=echo FILE=/bin/echo
# [14:30:03] PID= 12347 PPID= 12344 COMM=cat FILE=/bin/cat
代码逐行解析
| 代码片段 | 说明 |
|---|---|
BPF_PERF_OUTPUT(events) |
定义 perf 事件 Map,用于内核向用户空间推送事件 |
bpf_get_current_pid_tgid() >> 32 |
获取当前进程 PID(高 32 位为 tgid 即 PID) |
bpf_get_current_task() |
获取当前 task_struct 指针 |
bpf_probe_read_user_str() |
安全地从用户空间读取字符串 |
events.perf_submit() |
将事件数据提交到 perf buffer |
bpf.attach_kprobe() |
将 BPF 函数挂载到内核探针点 |
bpf["events"].open_perf_buffer() |
在用户空间打开 perf buffer 接收数据 |
bpf.perf_buffer_poll() |
轮询 perf buffer,触发回调函数 |
2.5.2 使用 libbpf+CO-RE 编写 Hello World
libbpf + CO-RE 是当前推荐的 eBPF 开发范式,具有编译一次到处运行的优势。
完整 C 代码示例
1. BPF 程序(hello.bpf.c)
/* hello.bpf.c - BPF 程序(运行在内核空间) */
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <bpf/bpf_core_read.h>
char LICENSE[] SEC("license") = "Dual BSD/GPL";
/* 定义事件数据结构 */
struct event {
u32 pid;
u32 ppid;
u64 ts_ns;
char comm[16];
};
/* 定义 Ring Buffer Map(高性能内核到用户空间通信) */
struct {
__uint(type, BPF_MAP_TYPE_RINGBUF);
__uint(max_entries, 256 * 1024); /* 256KB */
} events SEC(".maps");
/* 挂载到 execve 系统调用(使用 tracepoint) */
SEC("tp/syscalls/sys_enter_execve")
int handle_execve(struct trace_event_raw_sys_enter *ctx)
{
struct event *e;
/* 从 ring buffer 预留空间 */
e = bpf_ringbuf_reserve(&events, sizeof(*e), 0);
if (!e)
return 0;
/* 填充事件数据 */
e->pid = bpf_get_current_pid_tgid() >> 32;
e->ppid = bpf_get_current_task()->real_parent->tgid;
e->ts_ns = bpf_ktime_get_ns();
bpf_get_current_comm(&e->comm, sizeof(e->comm));
/* 提交事件 */
bpf_ringbuf_submit(e, 0);
return 0;
}
2. 用户空间加载器(hello.c)
/* hello.c - 用户空间加载器 */
#include <stdio.h>
#include <signal.h>
#include <time.h>
#include <sys/resource.h>
#include <bpf/libbpf.h>
#include <bpf/bpf.h>
#include "hello.skel.h" /* CO-RE Skeleton 头文件 */
static volatile sig_atomic_t exiting = 0;
static void sig_handler(int sig)
{
exiting = 1;
}
/* 事件结构(与 BPF 程序中定义一致) */
struct event {
unsigned int pid;
unsigned int ppid;
unsigned long long ts_ns;
char comm[16];
};
/* Ring Buffer 事件处理回调 */
static int handle_event(void *ctx, void *data, size_t data_sz)
{
const struct event *e = data;
struct tm *tm;
char ts[32];
time_t t;
time(&t);
tm = localtime(&t);
strftime(ts, sizeof(ts), "%H:%M:%S", tm);
printf("%-8s PID=%-7d PPID=%-7d COMM=%-16s\n",
ts, e->pid, e->ppid, e->comm);
return 0;
}
int main(int argc, char **argv)
{
struct ring_buffer *rb = NULL;
struct hello_bpf *skel;
int err;
/* 设置信号处理 */
signal(SIGINT, sig_handler);
signal(SIGTERM, sig_handler);
/* 设置 memlock 限制(内核 < 5.11 需要) */
struct rlimit rlim = {
.rlim_cur = RLIM_INFINITY,
.rlim_max = RLIM_INFINITY,
};
setrlimit(RLIMIT_MEMLOCK, &rlim);
/* 打开 BPF 程序(Skeleton 加载) */
skel = hello_bpf__open();
if (!skel) {
fprintf(stderr, "Failed to open BPF skeleton\n");
return 1;
}
/* 加载 BPF 程序到内核 */
err = hello_bpf__load(skel);
if (err) {
fprintf(stderr, "Failed to load BPF skeleton: %d\n", err);
goto cleanup;
}
/* 挂载 BPF 程序到 tracepoint */
err = hello_bpf__attach(skel);
if (err) {
fprintf(stderr, "Failed to attach BPF skeleton: %d\n", err);
goto cleanup;
}
printf("Successfully started! Tracing execve... (Ctrl+C to exit)\n");
printf("%-8s %-7s %-7s %s\n", "TIME", "PID", "PPID", "COMM");
/* 设置 Ring Buffer */
rb = ring_buffer__new(bpf_map__fd(skel->maps.events),
handle_event, NULL, NULL);
if (!rb) {
fprintf(stderr, "Failed to create ring buffer\n");
goto cleanup;
}
/* 事件循环 */
while (!exiting) {
err = ring_buffer__poll(rb, 100);
if (err == -EINTR) {
err = 0;
break;
}
if (err < 0) {
fprintf(stderr, "Error polling ring buffer: %d\n", err);
break;
}
}
cleanup:
ring_buffer__free(rb);
hello_bpf__destroy(skel);
return err != 0;
}
Makefile 构建脚本
# ===== Makefile for libbpf + CO-RE eBPF 程序 =====
# 使用: make # 编译
# make run # 编译并运行
# make clean # 清理
# make vmlinux.h # 生成 vmlinux.h
# 工具链
CLANG ?= clang
LLVM_STRIP ?= llvm-strip
BPFTOOL ?= bpftool
CC ?= gcc
# 目录
BUILD_DIR := build
# 编译参数
CFLAGS := -g -Wall -O2
BPF_CFLAGS := -O2 -g -target bpf -D__TARGET_ARCH_x86 \
-I/usr/include/$(shell uname -m)-linux-gnu \
-I.
# 目标文件
BPF_SRC := hello.bpf.c
BPF_OBJ := $(BUILD_DIR)/hello.bpf.o
BPF_SKEL := hello.skel.h
LOADER_SRC := hello.c
LOADER_OBJ := $(BUILD_DIR)/hello
# 默认目标
all: $(LOADER_OBJ)
# 生成 vmlinux.h
vmlinux.h:
$(BPFTOOL) btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h
# 编译 BPF 程序为字节码
$(BPF_OBJ): $(BPF_SRC) vmlinux.h
mkdir -p $(BUILD_DIR)
$(CLANG) $(BPF_CFLAGS) -c $< -o $@
$(LLVM_STRIP) -g $@
# 生成 Skeleton 头文件
$(BPF_SKEL): $(BPF_OBJ)
$(BPFTOOL) gen skeleton $< > $@
# 编译用户空间加载器
$(LOADER_OBJ): $(LOADER_SRC) $(BPF_SKEL)
mkdir -p $(BUILD_DIR)
$(CC) $(CFLAGS) $< -o $@ -lbpf -lelf -lz
# 编译并运行
run: $(LOADER_OBJ)
sudo ./$(LOADER_OBJ)
# 清理
clean:
rm -rf $(BUILD_DIR) $(BPF_SKEL) vmlinux.h
.PHONY: all clean run vmlinux.h
编译加载运行完整流程
# 1. 生成 vmlinux.h(CO-RE 依赖,仅需一次)
bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h
# 2. 编译 BPF 程序为字节码
clang -O2 -g -target bpf -D__TARGET_ARCH_x86 \
-I/usr/include/x86_64-linux-gnu -I. \
-c hello.bpf.c -o build/hello.bpf.o
# 3. 剥离调试信息(减小体积)
llvm-strip -g build/hello.bpf.o
# 4. 生成 Skeleton 头文件
bpftool gen skeleton build/hello.bpf.o > hello.skel.h
# 5. 编译用户空间加载器
gcc -g -Wall -O2 hello.c -o build/hello -lbpf -lelf -lz
# 6. 运行(需要 root 或 CAP_BPF)
sudo ./build/hello
# 一键编译运行(使用 Makefile)
make run
编译加载流程图
上图展示了 libbpf + CO-RE 的完整编译加载流程:编译阶段将 BPF C 源码通过 Clang 编译为 BPF 字节码,再用 bpftool 生成 Skeleton 头文件;运行阶段通过 Skeleton API 依次完成 open、load、attach 三步加载 BPF 程序到内核,最后通过 ring buffer 轮询接收内核事件。
2.5.3 使用 bpftrace 一行命令
bpftrace 提供了极其简洁的一行命令语法,无需编写完整程序即可快速追踪系统行为。
bpftrace 快速示例
# 1. 统计各进程的 read() 调用次数(按 Ctrl+C 结束查看结果)
sudo bpftrace -e 'tracepoint:syscalls:sys_enter_read { @[comm] = count(); }'
# 2. 追踪进程创建,打印进程名和 PID
sudo bpftrace -e 'tracepoint:syscalls:sys_enter_execve { printf("%s -> %s\n", comm, str(args->filename)); }'
# 3. 统计系统调用延迟分布(直方图)
sudo bpftrace -e 'kprobe:vfs_read { @start[tid] = nsecs; } kretprobe:vfs_read /@start[tid]/ { @latency = hist(nsecs - @start[tid]); delete(@start[tid]); }'
# 4. 追踪文件打开事件
sudo bpftrace -e 'tracepoint:syscalls:sys_enter_openat { printf("%s %s %s\n", comm, str(args->filename), str(args->mode)); }'
# 5. 按进程统计 TCP 连接
sudo bpftrace -e 'kprobe:tcp_v4_connect { @[comm] = count(); }'
常用 bpftrace 探针示例
# --- 进程相关 ---
# 追踪所有进程创建和退出
sudo bpftrace -e 'tracepoint:sched:sched_process_exec { printf("EXEC: %s pid=%d\n", comm, pid); } tracepoint:sched:sched_process_exit { printf("EXIT: %s pid=%d\n", comm, pid); }'
# --- 文件 I/O ---
# 追踪文件写入大小分布
sudo bpftrace -e 'tracepoint:syscalls:sys_enter_write { @bytes[comm] = hist(args->count); }'
# --- 网络 ---
# 统计 TCP 连接目标 IP
sudo bpftrace -e 'kprobe:tcp_v4_connect { $daddr = (struct sockaddr_in *)arg1; @conns[ntop($daddr->sin_addr.s_addr)] = count(); }'
# --- 内存 ---
# 统计缺页中断
sudo bpftrace -e 'tracepoint:exceptions:page_fault_user { @[comm] = count(); }'
# --- 信号 ---
# 追踪信号发送
sudo bpftrace -e 'tracepoint:signal:signal_generate { printf("Signal %d -> %s (pid=%d)\n", args->sig, str(args->comm), args->pid); }'
# --- 定时统计 ---
# 每 5 秒打印一次系统调用统计
sudo bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @[args->id] = count(); } interval:s:5 { print(@); clear(@); }'
# --- 运行 bpftrace 自带工具 ---
# 列出所有可用工具
ls /usr/share/bpftrace/tools/
# 运行工具(例如 biosnoop 追踪块设备 I/O)
sudo bpftrace /usr/share/bpftrace/tools/biosnoop.bt
2.6 验证环境完整性
2.6.1 完整环境验证脚本
以下脚本一次性检查所有 eBPF 组件是否正确安装和配置:
#!/bin/bash
# ===== eBPF 环境完整性验证脚本 =====
# 运行: sudo bash verify-ebpf-env.sh
# Author: Pozicaiman
# Date: 2026-07-24
set -euo pipefail
GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
PASS_COUNT=0
FAIL_COUNT=0
WARN_COUNT=0
pass() { echo -e "${GREEN}[PASS]${NC} $1"; ((PASS_COUNT++)); }
fail() { echo -e "${RED}[FAIL]${NC} $1"; ((FAIL_COUNT++)); }
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; ((WARN_COUNT++)); }
info() { echo -e "${BLUE}[INFO]${NC} $1"; }
echo "=========================================="
echo " eBPF 环境完整性验证"
echo " $(date '+%Y-%m-%d %H:%M:%S')"
echo "=========================================="
echo ""
# --- 1. 内核版本 ---
echo "--- 1. 内核版本检查 ---"
KV=$(uname -r)
info "内核版本: $KV"
KV_NUM=$(echo "$KV" | awk -F. '{print $1*100+$2}')
if [ "$KV_NUM" -ge 510 ]; then
pass "内核版本满足推荐要求 (>=5.10)"
else
warn "内核版本 $KV,建议升级到 5.10+"
fi
# --- 2. BTF 检查 ---
echo -e "\n--- 2. BTF 调试信息 ---"
if [ -f /sys/kernel/btf/vmlinux ]; then
pass "BTF 可用 (/sys/kernel/btf/vmlinux)"
else
fail "BTF 不可用,CO-RE 程序无法运行"
fi
# --- 3. JIT 检查 ---
echo -e "\n--- 3. BPF JIT ---"
JIT=$(cat /proc/sys/net/core/bpf_jit_enable 2>/dev/null || echo "0")
if [ "$JIT" = "1" ]; then
pass "BPF JIT 已启用"
else
fail "BPF JIT 未启用 (当前: $JIT)"
fi
# --- 4. 工具链检查 ---
echo -e "\n--- 4. 工具链检查 ---"
# Clang
if command -v clang &> /dev/null; then
CLANG_VER=$(clang --version | head -1)
pass "Clang: $CLANG_VER"
else
fail "Clang: 未安装"
fi
# LLVM
if command -v llc &> /dev/null; then
pass "LLVM: $(llc --version | head -1)"
else
fail "LLVM (llc): 未安装"
fi
# llvm-strip
if command -v llvm-strip &> /dev/null; then
pass "llvm-strip: 已安装"
else
fail "llvm-strip: 未安装"
fi
# libbpf
if pkg-config --exists libbpf 2>/dev/null; then
pass "libbpf: $(pkg-config --modversion libbpf)"
else
fail "libbpf: 未安装"
fi
# bpftool
if command -v bpftool &> /dev/null; then
pass "bpftool: $(bpftool version 2>&1 | head -1)"
else
fail "bpftool: 未安装"
fi
# bpftrace
if command -v bpftrace &> /dev/null; then
pass "bpftrace: $(bpftrace --version)"
else
warn "bpftrace: 未安装 (可选)"
fi
# BCC
if python3 -c "from bcc import BPF" 2>/dev/null; then
pass "BCC Python 绑定: 已安装"
else
warn "BCC Python 绑定: 未安装 (可选)"
fi
# --- 5. 内核配置检查 ---
echo -e "\n--- 5. 关键内核配置 ---"
CONFIG_SRC=""
if [ -f "/boot/config-$(uname -r)" ]; then
CONFIG_SRC="cat /boot/config-$(uname -r)"
elif [ -f "/proc/config.gz" ]; then
CONFIG_SRC="zcat /proc/config.gz"
fi
if [ -n "$CONFIG_SRC" ]; then
for cfg in CONFIG_BPF CONFIG_BPF_SYSCALL CONFIG_BPF_JIT CONFIG_DEBUG_INFO_BTF; do
val=$(eval "$CONFIG_SRC" 2>/dev/null | grep "^${cfg}=" | cut -d= -f2)
if [ "$val" = "y" ] || [ "$val" = "m" ]; then
pass "$cfg=$val"
else
fail "$cfg 未启用"
fi
done
else
warn "无法读取内核配置文件"
fi
# --- 6. memlock 限制 ---
echo -e "\n--- 6. 内存锁定限制 ---"
MEMLOCK=$(ulimit -l)
if [ "$MEMLOCK" = "unlimited" ] || [ "$MEMLOCK" -ge 1048576 ]; then
pass "memlock 限制充足 ($MEMLOCK)"
else
warn "memlock 限制较低 ($MEMLOCK KB)"
fi
# --- 7. 权限检查 ---
echo -e "\n--- 7. 权限检查 ---"
if [ "$(id -u)" = "0" ]; then
pass "当前为 root 用户"
else
if [ "$KV_NUM" -ge 511 ]; then
info "内核 5.11+ 支持 CAP_BPF,非 root 用户可使用能力运行"
else
warn "非 root 用户,且内核 < 5.11,需 root 运行 BPF 程序"
fi
fi
# --- 汇总 ---
echo -e "\n=========================================="
echo " 验证结果汇总"
echo "=========================================="
echo -e " ${GREEN}通过: $PASS_COUNT${NC}"
echo -e " ${RED}失败: $FAIL_COUNT${NC}"
echo -e " ${YELLOW}警告: $WARN_COUNT${NC}"
echo "=========================================="
if [ "$FAIL_COUNT" -gt 0 ]; then
echo -e "\n${RED}存在失败项,请根据上述提示修复后重新验证。${NC}"
exit 1
else
echo -e "\n${GREEN}环境验证通过!可以开始 eBPF 开发。${NC}"
exit 0
fi
2.6.2 运行测试 BPF 程序验证功能
使用一个最小化的 BPF 程序验证加载和运行功能:
#!/bin/bash
# ===== 测试 BPF 程序加载与运行 =====
echo "=== 测试 1: bpftool 探测内核特性 ==="
sudo bpftool feature probe | head -20
echo ""
echo "=== 测试 2: bpftrace 最小程序 ==="
# 运行一个简单的 bpftrace 程序,5秒后自动退出
sudo timeout 5 bpftrace -e 'BEGIN { printf("Hello eBPF! bpftrace works.\n"); exit(); }'
if [ $? -eq 0 ]; then
echo "[OK] bpftrace 运行正常"
else
echo "[FAIL] bpftrace 运行失败"
fi
echo ""
echo "=== 测试 3: BCC 最小程序 ==="
sudo python3 -c "
from bcc import BPF
bpf = BPF(text='int kprobe__sys_clone(void *ctx) { bpf_trace_printk(\"Hello from BCC!\\n\"); return 0; }')
print('[OK] BCC 程序加载成功')
import time; time.sleep(1)
" 2>&1 && echo "[OK] BCC 运行正常" || echo "[FAIL] BCC 运行失败"
echo ""
echo "=== 测试 4: 检查 BPF 程序是否在内核中 ==="
sudo bpftool prog show 2>/dev/null | head -10
echo ""
echo "=== 测试 5: Ring Buffer / Perf Buffer ==="
# 验证 ringbuf map 类型支持
sudo bpftool feature probe map_types 2>/dev/null | grep -i ringbuf
echo ""
2.6.3 性能基准测试
使用以下脚本测试 BPF 程序的执行延迟和事件吞吐量:
#!/bin/bash
# ===== eBPF 性能基准测试 =====
# 测量 BPF 程序的事件处理延迟和吞吐量
echo "=== eBPF 性能基准测试 ==="
echo "日期: $(date)"
echo "内核: $(uname -r)"
echo "CPU: $(grep 'model name' /proc/cpuinfo | head -1 | cut -d: -f2 | xargs)"
echo ""
# 测试 1: bpftrace 事件吞吐量(10秒内捕获的 syscall 数量)
echo "--- 测试 1: 系统调用吞吐量 (10秒) ---"
sudo timeout 10 bpftrace -e '
BEGIN { printf("开始统计系统调用...\n"); }
tracepoint:raw_syscalls:sys_enter { @count++; }
END { printf("10秒内系统调用总数: %d\n", @count); printf("平均每秒: %d\n", @count/10); clear(@count); }
' 2>&1
echo ""
# 测试 2: BPF 程序执行延迟(使用 kprobe 测量 nanoseconds)
echo -e "\n--- 测试 2: BPF 程序执行延迟 ---"
sudo timeout 5 bpftrace -e '
kprobe:vfs_read { @start[tid] = nsecs; }
kretprobe:vfs_read /@start[tid]/ {
@latency_ns = nsecs - @start[tid];
delete(@start[tid]);
}
END {
printf("vfs_read 延迟分布 (nanoseconds):\n");
print(@latency_ns);
clear(@latency_ns);
}
' 2>&1
echo ""
# 测试 3: BPF JIT 编译验证
echo -e "\n--- 测试 3: JIT 编译状态 ---"
echo "BPF JIT 启用状态: $(cat /proc/sys/net/core/bpf_jit_enable)"
echo "JIT 硬件卸载: $(cat /proc/sys/net/core/bpf_jit_harden)"
echo ""
echo "已加载 BPF 程序的 JIT 状态:"
sudo bpftool prog show 2>/dev/null | grep -E "id|name|xlated|jited" | head -20
echo ""
echo "=== 基准测试完成 ==="
性能参考值:在典型 x86_64 服务器上,单个 BPF 程序的执行延迟通常在 50-200 纳秒范围内。JIT 编译后的 BPF 程序性能接近原生代码,比解释执行快 2-4 倍。
2.7 常见安装问题与解决方案
2.7.1 内核版本不兼容问题
现象:加载 BPF 程序时报错 Invalid argument 或 Operation not supported,程序中使用了较新内核才支持的特性。
排查步骤:
# 1. 检查当前内核版本
uname -r
# 2. 使用 bpftool 探测内核支持的 BPF 特性
sudo bpftool feature probe
# 3. 检查 BPF 程序使用的 helper 函数是否被内核支持
sudo bpftool feature probe | grep "bpf_probe_read"
# 4. 查看 dmesg 中 BPF 相关错误
sudo dmesg | grep -i bpf | tail -20
根因:BPF 程序使用了当前内核版本不支持的特性(如 ring buffer 需要 5.8+,bpf_timer 需要 5.15+)。
解决方案:
| 问题场景 | 解决方案 |
|---|---|
| 内核版本过低 | 升级到 5.10+ LTS 内核(推荐 HWE 内核) |
| 缺少特定 helper 函数 | 降级使用旧 API(如 perf_buffer 替代 ringbuf) |
| Ubuntu 使用 HWE 内核 | sudo apt install linux-generic-hwe-22.04 |
| RHEL 激活新内核 | dnf install kernel-rt 或使用 ELRepo |
| 无法升级内核 | 使用 BCC(运行时编译,非 CO-RE)替代 libbpf |
# Ubuntu 升级 HWE 内核示例
sudo apt update
sudo apt install --install-recommends linux-generic-hwe-22.04
sudo reboot
# 重启后验证
uname -r
2.7.2 缺少 BTF 信息问题
现象:CO-RE 程序加载失败,报错 Error loading .BTF: No such file or directory 或 failed to find kernel BTF。
排查步骤:
# 1. 检查 BTF 文件是否存在
ls -la /sys/kernel/btf/vmlinux
# 2. 检查内核配置
grep CONFIG_DEBUG_INFO_BTF /boot/config-$(uname -r)
# 3. 尝试用 bpftool 读取 BTF
bpftool btf dump file /sys/kernel/btf/vmlinux format c | head -10
根因:内核编译时未启用 CONFIG_DEBUG_INFO_BTF=y,或 BTF 文件缺失。
解决方案:
# 方案 1:安装 BTF 信息包(部分发行版提供独立包)
# Ubuntu/Debian
sudo apt install linux-modules-extra-$(uname -r)
# 检查是否生成 BTF
ls /sys/kernel/btf/vmlinux
# 方案 2:使用 BTFHub 获取 BTF 信息(适用于无 BTF 的发行版)
git clone https://github.com/aquasecurity/btfhub.git
cd btfhub
# 生成当前内核的 BTF 文件
./btfhub.sh
# 方案 3:重新编译内核(启用 BTF)
# 需要安装 pahole (dwarves 包)
sudo apt install dwarves
# 在内核配置中启用
# CONFIG_DEBUG_INFO_BTF=y
# CONFIG_DEBUG_INFO=y
2.7.3 权限问题(CAP_BPF 配置)
现象:非 root 用户运行 BPF 程序报错 Operation not permitted。
排查步骤:
# 1. 检查非特权 BPF 是否被禁用
cat /proc/sys/kernel/unprivileged_bpf_disabled
# 0=允许非特权, 1=禁止(可改), 2=禁止(不可改)
# 2. 检查当前用户能力
capsh --print | grep bpf
# 3. 检查内核版本是否支持 CAP_BPF
uname -r # 需要 5.8+
解决方案:
# 方案 1:为可执行文件设置能力(内核 5.8+)
sudo setcap cap_bpf,cap_perfmon,cap_net_admin+ep ./hello
# 方案 2:使用 sudo 运行
sudo ./hello
# 方案 3:临时允许非特权 BPF(不推荐,安全风险)
sudo sysctl -w kernel.unprivileged_bpf_disabled=0
# 方案 4:将用户加入 bpf 组并配置 udev 规则
sudo groupadd bpf
sudo usermod -aG bpf $USER
# 配置 /etc/security/limits.conf 允许 bpf 组使用 memlock
2.7.4 内存锁定限制问题
现象:加载 BPF 程序报错 Unable to lock memory: Cannot allocate memory 或 RLIMIT_MEMLOCK 相关错误。
排查步骤:
# 检查当前 memlock 限制
ulimit -l
# 默认通常为 64KB 或 8MB
# 检查 BPF 程序使用的锁定内存
grep -i bpf /proc/meminfo
解决方案:
# 方案 1:临时调整(当前会话生效)
ulimit -l unlimited
# 方案 2:永久调整(limits.conf)
sudo tee -a /etc/security/limits.conf << 'EOF'
* soft memlock unlimited
* hard memlock unlimited
EOF
# 注销重新登录生效
# 方案 3:使用 CAP_BPF(内核 5.11+,不需要 memlock)
sudo setcap cap_bpf+ep ./hello
# 方案 4:systemd 服务配置
# 在 service 文件中添加:
# [Service]
# LimitMEMLOCK=infinity
2.7.5 编译错误排查
现象:编译 BPF 程序时出现各种编译错误。
常见编译错误与解决方案:
| 错误信息 | 原因 | 解决方案 |
|---|---|---|
error: unknown type name 'u32' |
缺少 vmlinux.h 或类型定义 | #include "vmlinux.h" |
error: 'BPF_MAP_TYPE_RINGBUF' undeclared |
内核版本过低或 libbpf 版本过低 | 升级 libbpf 到 1.0+ 或使用 perf_buffer |
clang: error: unknown target triple 'bpf' |
Clang 未编译 BPF 后端 | 安装支持 BPF 的 clang 版本 |
error: cannot find -lbpf |
libbpf 未安装或路径不对 | sudo apt install libbpf-dev |
error: 'bpf_ringbuf_reserve' undeclared |
libbpf 版本过低 | 升级 libbpf 到 0.6+ |
error: 'SEC' undeclared |
缺少 bpf_helpers.h | #include <bpf/bpf_helpers.h> |
BPF_VERIFY: invalid bpf_context access |
访问了错误的上下文字段 | 检查探针类型与上下文结构匹配 |
调试编译问题:
# 1. 检查 clang 是否支持 BPF target
echo 'int x = 0;' | clang -target bpf -c -x c - -o /dev/null && echo "BPF target OK"
# 2. 查看编译详细输出
clang -v -target bpf -c hello.bpf.c -o hello.bpf.o 2>&1
# 3. 检查生成的 BPF 对象文件
llvm-objdump -d hello.bpf.o | head -30
# 4. 使用 bpftool 检查 BPF 对象
bpftool prog loadall hello.bpf.o /sys/fs/bpf/hello type tracepoint 2>&1
# 5. 查看验证器日志(加载时加 -v 参数)
sudo ./hello -v # 或设置环境变量
验证器错误排查:
# 当 BPF 程序被验证器拒绝时,查看详细日志
# 在用户空间代码中设置调试输出:
# libbpf_set_print(bpf_printf);
# 或使用 bpftool 直接加载查看验证器日志
sudo bpftool prog load hello.bpf.o /sys/fs/bpf/test \
type tracepoint 2>&1
# 常见验证器错误:
# - "invalid bpf_context access" → 上下文字段访问错误
# - "back-edge in cfg" → 循环不受限(需要 #pragma unroll)
# - "0-size record" → 结构体大小为 0
# - "reached insn limit" -> 程序指令数超限(默认 100 万条)
2.8 本章小结
本章全面介绍了 eBPF 开发环境的搭建过程,涵盖从内核要求到工具链安装、从开发环境配置到第一个程序运行的完整流程。
核心要点回顾
| 知识点 | 关键内容 |
|---|---|
| 内核版本 | 推荐 5.10+ LTS,最低 4.18+;CO-RE 需要 5.0+ |
| 内核配置 | 必须启用 CONFIG_BPF、CONFIG_BPF_SYSCALL、CONFIG_BPF_JIT、CONFIG_DEBUG_INFO_BTF |
| BTF 信息 | CO-RE 的基础依赖,通过 /sys/kernel/btf/vmlinux 检查可用性 |
| Clang/LLVM | 推荐 12+,用于将 C 源码编译为 BPF 字节码 |
| libbpf | 推荐 1.0+,CO-RE 程序加载库,向后兼容旧内核 |
| BCC | 高层开发框架,Python 绑定,适合快速原型和运维工具 |
| bpftrace | 一行命令式追踪语言,适合快速排障和临时分析 |
| bpftool | 内核官方 BPF 管理工具,用于查看/加载/调试 BPF 程序 |
| CO-RE | Compile Once - Run Everywhere,基于 BTF 实现跨内核版本兼容 |
| CAP_BPF | 内核 5.8+ 引入,5.11+ 允许非特权用户运行 BPF 程序 |
| 容器化 | Docker 需要特权模式或 CAP_BPF;K8s 使用 DaemonSet 部署 |
环境搭建快速路径
根据不同需求,推荐以下环境搭建路径:
下章预告
本章完成了 eBPF 开发环境的搭建和验证。第三章将深入 eBPF 的核心概念,包括:
- eBPF 架构详解:验证器、JIT 编译器、BPF 虚拟机的工作原理
- BPF 程序类型:kprobe、tracepoint、XDP、tc、cgroup 等各类型详解
- BPF Map 类型:Hash、Array、Ring Buffer、PerCPU 等数据结构
- BPF Helper 函数:内核提供的辅助函数库
- CO-RE 机制深入:BTF、重定位、Skeleton 的底层原理
实践建议:在进入第三章前,建议读者完整运行本章的验证脚本和 Hello World 程序,确保开发环境完全就绪。后续章节的代码示例均基于本章搭建的环境。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐



所有评论(0)