如何在 Ubuntu 上配置 eBPF 开发环境
我的环境
我的电脑: MacBook Pro (14-inch, 2021), Ventura 13.2, M1 Max (ARM64,aarch64)
本地 Linux 开发环境: Ubuntu 22.04.2 LTS (5.15.0-60-generic) in Parallels Desktop 18 for Mac
在这里我建议选择大于 5.0.0 内核版本的 Linux 发行版。可以使用阿里云、腾讯云、Google、AWS、等公有云上的 Ubuntu。也可以在你本地电脑上使用 Parallels Desktop 18 for Mac、Vargrant、等工具创建 Ubuntu 虚拟机。
下面安装过程均在 Ubuntu 22.04.2 LTS (5.15.0-60-generic) 进行测试。
一键安装
如果没有特殊需求或者只是在尝试学习使用 eBPF 可以使用以下命令安装:
sudo apt install -y make clang llvm libelf-dev libbpf-dev bpfcc-tools libbpfcc-dev
sudo apt install -y linux-tools-$(uname -r) linux-headers-$(uname -r) linux-tools-commonLLVM & CLANG
默认安装
sudo apt install clang llvm lld libclang-dev libllvm llvm-dev使用官方源安装
你可以使用添加官方源的方式在 Ubuntu 上安装指定版本的 LLVM:
安装 GPG 证书
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key|sudo apt-key add -写入软件源信息,将下面内容添加到 /etc/apt/sources.list
deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy main
deb-src http://apt.llvm.org/jammy/ llvm-toolchain-jammy main
# 15
deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-15 main
deb-src http://apt.llvm.org/jammy/ llvm-toolchain-jammy-15 main
# 16
deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-16 main
deb-src http://apt.llvm.org/jammy/ llvm-toolchain-jammy-16 main更新
sudo apt-get update安装 LLVM 工具链
sudo apt install -y clang-15 lldb-15 lld-15 clangd-15使用官方脚本安装(推荐)
默认安装
wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
sudo ./llvm.sh指定版本号
wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
sudo ./llvm.sh 15默认安装所有工具链
wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
sudo ./llvm.sh all指定版本安装所有工具链
wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
sudo ./llvm.sh 16 all如果在 Ubuntu 上使用 apt 安装 LLVM 的更多方法可以参考 APT LLVM 页面
如果你需要使用二进制包安装特定版本的 LLVM,可以在 LLVM 下载页面 上下载相应的版本并手动安装。
查看安装路径
如果使用脚本安装,默认情况下会将工具链保存到 /lib/llvm-{版本}/bin 下面。并且会将所有的命令创建软连接到 /usr/bin/ 下面
查看安装目录
ls -ls /lib/llvm-16/bin/查看软连接
ls -ls /usr/bin/llvm-*
ls -ls /usr/bin/clang*LLVM 可以多版本共存,但是需要切换环境变量。以免使用不符合要求的版本进行编译。我就在我的开发机器上安装了多个版本的 LLVM。
ls -l /lib/ |grep llvm
drwxr-xr-x 3 root root 4096 Mar 2 22:25 llvm-11
drwxr-xr-x 7 root root 4096 Feb 25 21:25 llvm-14
drwxr-xr-x 7 root root 4096 Mar 29 22:25 llvm-15
drwxr-xr-x 7 root root 4096 Mar 29 23:15 llvm-16使用环境变量管理 LLVM 及切换默认的 LLVM
将下面内容保存到 $HOME/.bashrc 中 并且使用 source $HOME/.bashrc 生效该环境变量。
export LLVM_PATH="/lib/llvm-15"
export PATH="$LLVM_PATH/bin:$PATH"使用 update-alternatives 管理多版本 文章最后会举例说一下这个命令的使用。
bpf.h
bpf.h 是 Linux 内核的一部分,通常在安装 Linux 操作系统时就已经包含在内核中。因此,如果你已经安装了 Linux 系统,通常就已经拥有了 bpf.h 头文件。但是,不同的 Linux 发行版可能会有不同的内核版本和配置,因此可能会存在某些版本的内核中不包含 bpf.h 头文件的情况。
如果你想知道你的 Linux 上是否安装了 bpf.h 可以使用 find 命令
sudo find /usr/include/ -name bpf.h输出结果
/usr/include/linux/bpf.h如果没有找到 bpf.h 在这种情况下,你需要安装相应的 Linux 内核头文件包,以便在开发和编译 eBPF 程序时能够访问 bpf.h 头文件。
使用包管理工具安装
通常情况下,你可以使用包管理来安装 Linux 内核头文件包。例如,在 Ubuntu 系统上,你可以使用以下命令安装 Linux 内核头文件包:
sudo apt install linux-headers-$(uname -r)这个命令将安装当前正在运行的内核版本对应的头文件包。安装完成后,你就可以在编写 eBPF 程序时使用 bpf.h 头文件了。
libbpf
libbpf 是一个用于管理和操作 eBPF 代码的库。它提供了一组低级别的 API,用于加载、卸载和管理 eBPF 程序和映射,以及与内核中的 eBPF 子系统进行交互。
libbpf 库的主要功能包括:
加载和卸载
eBPF程序和映射与
eBPF程序和映射进行交互,例如查看和修改它们的属性与内核中的
eBPF子系统进行交互,例如获取eBPF子系统的信息和状态生成
eBPF代码和数据的 ELF 格式文件,以便将其编译为内核模块或用户空间程序
libbpf 库的源代码托管在 GitHub 上,可以访问 libbpf Github 查看源代码。
安装依赖
sudo apt install pkg-config使用源码编译安装
克隆源码
git clone https://github.com/libbpf/libbpf.git
cd libbpf编译安装(默认路径)
cd src
// 编译
sudo make
// 编译安装
sudo make install输出结果
INSTALL bpf.h libbpf.h btf.h libbpf_common.h libbpf_legacy.h bpf_helpers.h bpf_helper_defs.h bpf_tracing.h bpf_endian.h bpf_core_read.h skel_internal.h libbpf_version.h usdt.bpf.h
INSTALL ./libbpf.pc
INSTALL ./libbpf.a ./libbpf.so ./libbpf.so.1 ./libbpf.so.1.2.0使用包管理工具安装
sudo apt install libbpf-dev查看安装路径
libbpf 库默认安装在 /usr/lib 目录下。
如果需要查看 libbpf.so 的具体安装位置,可以使用 find 命令:
sudo find /usr/lib -name libbpf.so
// or
sudo find /lib --name libbpf.so头文件默认安装在 /usr/include/bpf/ 目录下。可以使用 ls 命令查看:
ls -ls /usr/include/bpf/bcc
bcc 是一组用于 eBPF 开发的工具,它包括了一组用于编写和调试 eBPF 程序的库和命令行工具。使用 bcc,可以更加方便地开发和调试 eBPF 程序,提高开发效率和代码质量。
使用包管理工具安装
在 Ubuntu 上,可以使用以下命令安装 bcc:
sudo apt update
sudo apt install bpfcc-tools这个命令将安装 bcc 工具集及其依赖项。
使用源码编译安装
如果你想自行编译 bcc 工具集,可以从 GitHub 上获取源代码并编译、我建议选择一个已经发布的 release 版本进行编译。
编译 bcc 工具集需要一些依赖项,包括 clang、llvm、libelf、libbfd 等。在编译 bcc 之前,需要先安装这些依赖项。
安装依赖
sudo apt install cmake
sudo apt install arping netperf iperf
sudo apt install bison flex
sudo apt install libdebuginfod-dev \
liblzma-dev \
libluajit-5.1-dev \
libcurl4-openssl-dev \
libelf-dev \
libedit-dev \
zlib1g-dev \
libfl-dev \
build-essential如果你的机器没有 Python 环境则需要安装 Python。
sudo apt install python3-distutils python3请确定你已经安装了 CLANG && LLVM 工具链并已配置好默认的环境变量。如果没有安装请查看前面的内容。
克隆仓库
git clone https://github.com/iovisor/bcc.git切换到已经发布的 release 版本
cd bcc
git checkout tags/v0.26.0编译
mkdir build; cd build
cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DENABLE_LLVM_SHARED=1
make
sudo make install安装 Python 模块绑定
cmake -DPYTHON_CMD=python3 .. # build python3 binding
pushd src/python/
make
sudo make install
popd查看安装路径
我们根据以上 编译 步骤可以看日志输出。了解具体安装过程及安装到哪些目录。
以上命令将下载 bcc 源代码并编译安装 bcc 工具集。编译完成后,你就可以在 /usr/share/bcc/tools 目录下找到各种 bcc 工具的源代码和示例程序。比如:
可以执行 sudo python3 /usr/share/bcc/tools/execsnoop 命令来运行 bcc 自带的 execsnoop 工具。
可以使用 python3 和 bcc 库进行开发
动态库会安装到 /usr/lib/aarch64-linux-gnu/ 目录下
ls /usr/lib/aarch64-linux-gnu/libbcc*工具集会安装到 /usr/share/bcc/ 目录下
ls -ls /usr/share/bcc/头文件会安装到 /usr/include/bcc/ 目录下
ls -ls /usr/include/bcc/如果想了解更多关于 bcc 的信息,可以查看 bcc GitHub 或者 bcc 官方文档 。
bpftool
bpftool 是一个用于管理和调试 eBPF 代码的命令行工具。它允许你查看和分析系统中运行的 eBPF 程序和映射,以及与内核中的 eBPF 子系统进行交互。更多内容可以查看 bpftool Github
使用 bpftool,你可以执行以下操作:
列出当前系统中所有加载的
eBPF程序和映射查看指定
eBPF程序或映射的详细信息,例如指令集、内存布局等修改
eBPF程序或映射的属性,例如禁用一个程序或清空一个映射将一个
eBPF程序或映射导出到文件中,以便在其他系统上重新导入调试
eBPF程序,例如跟踪程序的控制流、访问内存等
使用包管理工具安装
sudo apt install -y linux-tools-$(uname -r)使用源码编译安装
安装依赖
sudo apt install build-essential \
libelf-dev \
libz-dev \
libcap-dev \
binutils-dev \
pkg-config请确定你已经安装了 CLANG && LLVM 工具链并已配置好默认的环境变量。如果没有安装请查看前面的内容。
克隆仓库
git clone https://github.com/libbpf/bpftool.git
cd bpftool
git submodule update --init编译
cd src
sudo make编译结果如下
... libbfd: [ on ]
... clang-bpf-co-re: [ on ]
... llvm: [ on ]
... libcap: [ on ]
...
...
CC struct_ops.o
CC tracelog.o
CC xlated_dumper.o
CC disasm.o
LINK bpftool安装到本机
sudo make install默认情况下 bpftool 命令会安装到/usr/local/sbin/ 下
验证命令
sudo bpftool v -p
// 输出结果
{
"version": "7.2.0",
"libbpf_version": "1.2",
"features": {
"libbfd": false,
"llvm": true,
"skeletons": true,
"bootstrap": false
}
}关于 libbfd = false 在 https://github.com/libbpf/bpftool/releases/tag/v7.1.0 版本说明如下:
增加对使用 LLVM 库(而不是 libbfd)反汇编 JIT 编译程序的支持,并默认切换到 LLVM。如果在构建 bpftool 时不存在 LLVM 库,则仍支持使用 libbfd 进行反汇编作为后备方案
Add support for disassembling JIT-compiled programs with the LLVM library (instead of libbfd), and switch to LLVM by default. Disassembling with libbfd is still supported as a fallback if the LLVM library is not present when building bpftool.
如果你想使用对 libbfd 的支持 可以使用 v7.0.0 版本。步骤如下:
克隆仓库并切换到 v7.0.0 版本
git clone https://github.com/libbpf/bpftool.git
cd bpftool
git submodule update --init
git checkout tags/v7.0.0编译
cd src
sudo make验证命令
sudo ./bpftool v -p
{
"version": "7.0.0",
"libbpf_version": "1.0",
"features": {
"libbfd": true,
"libbpf_strict": true,
"skeletons": true
}
}使用 Linux 源码编译安装安装依赖
安装依赖
sudo apt install build-essential \
libelf-dev \
libz-dev \
libcap-dev \
binutils-dev \
pkg-config如果你已经安装了 CLANG && LLVM 工具链。只要配置好默认的环境变量即可。如果没有安装请查看前面的内容
克隆仓库
在这里建议使用与当前内核版本匹配或兼容的版本。您可以通过运行 uname -r 来检查当前内核版本,并使用 apt-cache search 搜索可用的 Linux 源代码版本。一旦找到所需版本,就可以继续安装过程。
查看当前内核版本
uname -r搜索 Linux 源码
sudo apt-cache search linux-source安装 Linux 源码
默认会将源码安装到 /usr/src/ 目录下
sudo apt install linux-source-5.15.0解压
cd /usr/src/
sudo tar xf linux-source-5.15.0.tar.bz2编译
cd linux-source-5.15.0/tools
cd bpf/bpftool
make验证命令
sudo ./bpftool v -p
{
"version": "5.15.87",
"features": {
"libbfd": true,
"skeletons": true
}
}linux-tools
linux-tools 包是 Linux 内核源码中包含的一组工具集,它包括了许多用于性能分析、调试和监测的工具,例如 perf、ftrace、bpftrace 等。这些工具可以帮助开发者深入了解系统内部的行为和性能瓶颈,从而优化程序和系统性能。
要查看当前系统中已经安装的 linux-tools 工具,可以使用以下命令:
dpkg -l | grep linux-tools这个命令将列出当前系统中所有已安装的 linux-tools 包及其版本号。如果系统中没有安装 linux-tools 包,可以使用包管理器安装,例如在 Ubuntu 系统中可以使用以下命令安装:
sudo apt install linux-tools-$(uname -r)其中,$(uname -r) 表示当前正在使用的内核版本号,可以保证安装的 *linux-tools 版本与当前内核版本相匹配。安装完成后,可以再次使用上述命令来检查 linux-tools 包是否已经安装成功。
需要注意的是,不同版本的 Linux 内核可能会包含不同的 linux-tools 工具集,且工具名称和版本也可能会有所不同。在使用 ``linux-tools` 工具时,建议先了解对应版本的工具集和使用方法,以便更好地使用和理解这些工具。
vmlinux.h
vmlinux.h 文件是 Linux 内核的一个头文件,通常包含在 Linux 内核头文件包中。如果你想检查系统中是否安装了 vmlinux.h 文件,可以使用 find 命令:
find /usr/include -name vmlinux.h这个命令将在 /usr/include 目录下查找 vmlinux.h 文件,如果存在,则表示系统中已经安装了 Linux 内核头文件包并且包含了 vmlinux.h 文件。如果未找到 vmlinux.h 文件,则需要安装 Linux 内核头文件包或者手动安装相应的头文件。
另外,需要注意的是,不同的 Linux 发行版可能会有不同的内核版本和配置,因此可能会存在某些版本的内核中不包含 vmlinux.h 头文件的情况。在这种情况下,你需要手动编译和安装相应的内核头文件,或者从其他渠道获取 vmlinux.h 文件。
生成 vmlinux.h
bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h使用 bpftool 工具可以生成 vimlinux.h
libbfd
如果你想检查是否安装了 libbfd 库,可以使用以下命令:
ldconfig -p | grep libbfd这个命令将列出系统中所有已安装的共享库,并过滤出包含关键字 libbfd 的库。如果输出结果中包含 libbfd,则表示 libbfd 库已经安装在系统中。
另外,如果你要在程序中使用 libbfd 库,可以使用以下命令检查是否存在 bfd.h 头文件:
find /usr/include -name bfd.h这个命令将在 /usr/include 目录下查找 bfd.h 文件,如果存在,则表示 libbfd 库已经安装在系统中并且可用于编译程序。如果未找到 bfd.h 文件,则需要安装 libbfd 库或者手动安装相应的头文件。
Rust
安装 Rust
打开终端并输入以下命令以安装
sudo curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh配置临时环境变量
source $HOME/.cargo/env使环境变量永久生效
echo "export PATH='$HOME/.cargo/bin:$PATH'" >> ~/.bashrc
// 或者
exho '. "$HOME/.cargo/env"' > .bashrc
// 生效
source .bashrc确认已安装 Rust
rustc --version以上步骤完成后,您已成功安装 Rust 。
FAQ
<asm/types.h>
如果在您的环境中找不到 <asm/types.h> 头文件,请根据您的内核版本和架构下载相应的内核头文件包并安装
sudo apt install linux-headers-$(uname -r)如果您的内核版本或架构与上述示例不同,请相应地替换版本和架构信息。安装完成后,您应该能够在 /usr/src/linux-headers-5.15.0-60-generic/include/asm 目录下找到 <types.h> 头文件。如果仍然找不到,请参考相关文档或社区寻求帮助。
sudo ln -s /usr/include/asm /usr/include/$(uname -m)-linux-gnu/asm编译依赖
bpftool 依赖问题
如果在编译 bpftool 命令时候出现和 CLANG && LLVM 依赖为 off 的情况
... libbfd: [ on ]
... clang-bpf-co-re: [ off ]
... llvm: [ off ]
... libcap: [ on ]
...
...则是因为在编译时期没有找到 llvm-config、llvm-strip、clang 上面内容在安装 CLANG && LLVM 工具链的地方讲了如何配置环境变量。在这里我则使用 update-alternatives 管理这几个依赖。
update-alternatives 命令可以轻松地在系统中安装和管理多个版本的 clang 编译器。以下是一个示例,说明如何使用 update-alternatives 将 clang-16 设置为系统中的默认编译器:
首先,检查系统中是否安装了 clang-16 编译器。可以使用以下命令来检查:
clang-16 --version如果 clang-16 编译器已经安装在系统中,则该命令应该输出编译器的版本信息。
使用 update-alternatives 命令来注册 clang-16 编译器。可以使用以下命令:
sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-16 100该命令将在 /usr/bin 目录下创建 clang 符号链接,并将其链接到 clang-16 可执行文件。100 表示此配置的优先级,表示此配置应该是优先级最高的。
现在,将 clang-16 编译器设置为默认编译器。可以使用以下命令:
sudo update-alternatives --set clang /usr/bin/clang-16该命令将 /usr/bin/clang 符号链接设置为链接到 clang-16 可执行文件,这样系统中的所有程序都将使用 clang-16 编译器进行编译。
可以使用以下命令来验证 clang 编译器的版本:
clang --version如果一切正常,该命令应该输出 clang-16 编译器的版本信息。
使用 pdate-alternatives 命令来管理不同版本的 llvm-config 工具,以便在需要时进行切换。以下是一个示例,说明如何在系统中安装和管理多个版本的 llvm-config 工具:
首先,检查系统中是否安装了 llvm-config 工具。可以使用以下命令来检查:
llvm-config --version如果 llvm-config 工具已经安装在系统中,则该命令应该输出工具的版本信息。
使用 update-alternatives 命令来注册 llvm-config 工具。可以使用以下命令:
sudo update-alternatives --install /usr/bin/llvm-config llvm-config /usr/bin/llvm-config-16 100该命令将在 /usr/bin 目录下创建 llvm-config 符号链接,并将其链接到 llvm-config-16 可执行文件。100 表示此配置的优先级,表示此配置应该是优先级最高的。
现在,将 llvm-config-16 工具设置为默认工具。可以使用以下命令:
sudo update-alternatives --set llvm-config /usr/bin/llvm-config-16该命令将 /usr/bin/llvm-config 符号链接设置为链接到 llvm-config-16 可执行文件,这样系统中的所有程序都将使用 llvm-config-16 工具进行配置。
可以使用以下命令来验证 llvm-config 工具的版本:
llvm-config --version如果一切正常,该命令应该输出 llvm-config-16 工具的版本信息。
使用 update-alternatives 命令来管理不同版本的 llvm-strip 工具。以下是一个示例,说明如何在系统中安装和管理多个版本的 llvm-strip 工具:
首先,检查系统中是否安装了 llvm-strip 工具。可以使用以下命令来检查:
llvm-strip --version如果 llvm-strip 工具已经安装在系统中,则该命令应该输出工具的版本信息。
使用 update-alternatives 命令来注册 llvm-strip 工具。可以使用以下命令:
sudo update-alternatives --install /usr/bin/llvm-strip llvm-strip /usr/bin/llvm-strip-16 100该命令将在 /usr/bin 目录下创建 llvm-strip 符号链接,并将其链接到 llvm-strip-16 可执行文件。100 表示此配置的优先级,表示此配置应该是优先级最高的。
现在,将 llvm-strip-16 工具设置为默认工具。可以使用以下命令:
sudo update-alternatives --set llvm-strip /usr/bin/llvm-strip-16该命令将 /usr/bin/llvm-strip 符号链接设置为链接到 llvm-strip-16 可执行文件,这样系统中的所有程序都将使用 llvm-strip-16 工具进行配置。
可以使用以下命令来验证 llvm-strip 工具的版本:
llvm-strip --version如果一切正常,该命令应该输出 llvm-strip-16 工具的版本信息。
请注意,update-alternatives 命令需要使用 sudo 权限运行,以便在系统级别创建符号链接。还需要根据您的系统设置相应的路径和权限。此外,如果您的系统中已经安装了多个版本的 LLVM 或者 CLANG 工具,则可以使用相同的方法将它们注册到 `update-alternatives 中,并在需要时进行切换。