资讯动态

XDP部署报错“No bpffs found”?从挂载到namespace的完整排查修复指南

发布时间:2026/10/19 2:52:01 来源:尧图企业网站定制
最近在一台新装机器上部署XDP数据面时脚本跑到一半就停了终端只有一行libxdp: No bpffs found at /sys/fs/bpf。我的第一反应是bpffs没挂载可是执行mount | grep bpf又明明能看到/sys/fs/bpf。反复确认了几次我才意识到这个报错的意思比字面上要宽得多它说的是 libxdp 在当前进程视角下没有找到可用的 bpffs而不一定是文件系统真的不存在。这篇笔记就把我完整排查和修复的过程写下来包括 libxdp 对 bpffs 的依赖逻辑、mount namespace 的坑、systemd 和容器环境下的差异以及一套能直接抄的检查脚本希望能给同样卡在这个报错上的人省点时间。1. 先搞清 libxdp 和 bpffs 之间到底是什么关系1.1 XDP程序为什么要钉在文件系统上XDP 程序运行在内核里但它本质上是 BPF 对象。BPF 对象是由用户态进程通过系统调用创建的寿命和引用计数挂钩如果创建它的进程退出、文件描述符关闭又没有其他引用这个对象就会被内核销毁。对 XDP 场景来说一个数据面程序往往需要先加载 BPF program再创建一堆 BPF map随后 AF_XDP 套接字或用户态控制面还要持续访问这些 map。如果进程一退出对象就没了整个数据面就没法管理。解决办法就是pin钉住。内核允许把 BPF 对象固定到 BPF 文件系统bpffs上只要 pin 文件还在对象就活着。XDP 加载器通常会把 program 和 map 都 pin 到/sys/fs/bpf下面的某个子目录比如/sys/fs/bpf/xdp/...。这样第一个加载程序退出后后续的管理进程、监控进程仍然能通过这些路径找到对象。可以理解成bpffs 是内核 BPF 对象之间互相传递的公共白板。1.2 libxdp 初始化时藏在最前面的路径检查libxdp 是 xdp-tools 的核心库常配套的xdp-loader只是它的命令行外壳。加载器启动后不是立刻发 BPF 系统调用而是要先把运行环境准备好检查 bpffs 是否可用、创建自己的目录、清理上次残留的 pin 文件等。在这个准备阶段libxdp 第一步就是去检测约定路径/sys/fs/bpf。正常情况下bpffs 挂载在这个路径后加载器会往里写东西如果检测失败它就认为没有可用的 bpffs于是直接抛出libxdp: No bpffs found at /sys/fs/bpf然后终止。也就是说这条报错本质是一个环境前提检查失败而不是 BPF 程序本身编译或加载出错。想解决它得从环境入手。1.3 同一个报错三种完全不同的潜台词我在排查过程中发现这条信息背后至少有三个梯队的原因而且它们表现很像原因梯队具体表现典型场景bpffs 真的没挂载/proc/mounts里找不到 bpf 类型挂载新装系统、初始化脚本没做系统准备、内核模块加载顺序问题挂载了但当前进程看不见宿主机有/sys/fs/bpf进程所在 namespace 里没有systemd 服务开了 PrivateMounts、容器未 bind mount挂载点可见但不可访问路径存在但 ls/touch 报 Permission denied 或 Read-only file system普通用户无权限、LSM 策略拦截、挂载选项为 ro这个分类帮我快速缩小了范围。后面所有排查步骤基本都是在判断当前环境到底属于哪一类。2. 我的排查链路从常规检查一路挖到 namespace2.1 先做一个挂载状态快照排查的第一步永远是收集事实别猜。我一般同时执行下面几条命令mount | grep bpf cat /proc/mounts | grep bpf ls -ld /sys/fs/bpf stat -f -c %T /sys/fs/bpf如果mount和cat /proc/mounts完全没有 bpf 相关行说明当前 namespace 根本没挂载。如果ls -ld /sys/fs/bpf能输出目录信息同时stat -f -c %T返回bpf说明这个路径确实是 bpffs 挂载点。如果ls报Permission denied问题就偏向权限而非挂载缺失。注意一点mount命令读的是/proc/mounts它显示的是当前挂载命名空间里的挂载表。如果你是用 SSH 登到宿主机上执行的那只是宿主机视角不代表故障进程也看得到。这也是很多人第一步被误导的原因。2.2 宿主机和故障进程的视角对比发现宿主机有挂载但依然报错后下一步就是切换到故障进程的视角去看。最直接的办法是用nsenter进入目标进程的挂载命名空间# 宿主机视角 grep bpf /proc/mounts # 目标进程视角PID 换成实际值 sudo nsenter -t 1234 -m grep bpf /proc/mounts如果宿主机有、目标进程没有结论已经出来了不是 bpffs 不存在而是当前进程的 mount namespace 里根本没有这个挂载。我还可以再看两个 namespace 的编号进一步确认是不是同一命名空间readlink /proc/self/ns/mnt readlink /proc/1234/ns/mnt如果两个链接指向的 inode 号不同说明它们确实生活在不同的挂载命名空间里。后面所有排查重点都放到这个进程为什么看不到宿主挂载上而不是急着去 mount。2.3 权限和挂载属性也不能跳过即使 bpffs 挂载可见如果当前用户访问不了结果依然可能表现为找不到。我一般会随手用普通用户和 sudo 各执行一次ls -ld /sys/fs/bpf sudo ls -ld /sys/fs/bpf touch /sys/fs/bpf/.write_test sudo touch /sys/fs/bpf/.write_test很多发行版里/sys/fs/bpf的默认权限是drwx------ root root普通用户无法写。如果程序本身是为了给普通用户运行的这个权限问题迟早会暴露。另外挂载选项里如果有ro即使 root 也没法写文件touch会直接报Read-only file system。顺带提一句SELinux、AppArmor 这类 LSM 也可能拦截对 bpffs 的访问但那个通常会报Permission denied或Operation not permitted而不是这条No bpffs found。所以它一般不是主因但我也见过策略导致 libxdp 在探测路径时拿不到完整信息的情况因此不该完全忽略。2.4 顺手确认内核 BPF 配置如果挂载命令本身都失败了那要去看内核配置。BPF 文件系统不是凭空就能 mount 的内核必须开了CONFIG_BPF_SYSCALL。常见的检查方式zcat /proc/config.gz 2/dev/null | grep -E CONFIG_BPF # 如果 /proc/config.gz 不存在就看 /boot/config-$(uname -r) grep -E CONFIG_BPF /boot/config-$(uname -r)重点关注几个选项CONFIG_BPF_SYSCALL、CONFIG_BPF_JIT、CONFIG_DEBUG_INFO_BTF。如果CONFIG_BPF_SYSCALLn那真的没法用 bpffs问题已经不是挂载能解决的了。不过我实际遇到的绝大多数机器都是开启的所以这一步通常排在后面避免一上来就在内核上浪费半天。3. 根因定位为什么明明挂在那里却找不到3.1 挂载从来不是全局的它是进程视角的很多刚接触 Linux 的人会默认挂了文件系统所有进程都能看到其实不对。挂载是挂载命名空间里的属性。每个进程都有一份自己的挂载表它可能来自父进程的继承也可能带着一堆私有的增删。用unshare(mount)或者某些容器运行时创建新命名空间后新空间默认会复制父空间当时的挂载状态但之后父空间新挂载的东西不会自动同步进去除非双方设置了挂载传播。回到这个报错如果宿主机的 systemd 或管理员后来把 bpffs 挂到了/sys/fs/bpf而你的程序是在挂载之前就进入了一个私有 mount namespace那么在它眼里这个挂载点就是不存在的。不是系统坏了是相机的镜头不对。3.2 systemd 服务里最容易踩的 PrivateMounts 陷阱systemd 默认会给一部分服务设置独立的挂载命名空间尤其是当你写了这类配置时[Service] PrivateMountsyes或者某些安全加固选项组合起来systemd 会让服务进程带上MountFlagsslave、PrivateMountsyes之类的属性。服务进程启动后宿主机的 bpffs 挂载不一定还是可见的。如果 xdp-loader 是被 systemd 拉起来的我用 systemd-run 临时复现过systemd-run --wait --propertyPrivateMountsyes sh -c mount | grep bpf || echo NO_BPFFS如果输出NO_BPFFS那基本实锤了。解决办法也很简单把服务单元里多余的私有挂载选项去掉或者在服务启动前确保 bpffs 已经挂载好更稳妥的做法是让服务依赖一个专门的sys-fs-bpf.mount单元保证启动顺序正确。3.3 容器场景不 bind mount 就别想看见容器和 systemd 服务的逻辑是一样的只不过更明显。容器默认不会继承宿主机后来新增的挂载点就算挂载命名空间是新建的、初始状态复制自宿主机也只在创建容器那一刻复制。如果容器启动命令里没有把/sys/fs/bpfbind 进去容器内就是看不到。正确的容器用法是在启动时显式挂载container run --rm -it \ --mount typebind,src/sys/fs/bpf,dst/sys/fs/bpf \ your-image bash这里多说一句不要指望只给--privileged就能解决。特权模式只是给了容器更多 capability不代表它自动继承宿主所有的挂载点。要访问 bpffs最干净的方式仍然是显式 bind mount。3.4 libxdp 找的是约定路径不是任意一个 bpffs还有一个经常被忽略的点libxdp 的探测逻辑通常是直奔/sys/fs/bpf不会像grep /proc/mounts那样去扫描系统里所有的 bpf 挂载。如果你图方便把 bpffs 挂到了/mnt/bpf或者/run/bpffs然后继续用 xdp-loader它照样会报No bpffs found at /sys/fs/bpf。听起来很蠢但很多脚本为了不污染系统根目录确实会这么干。如果已经挂到别的路径可以把标准路径变成 bind 挂载sudo mkdir -p /sys/fs/bpf sudo mount --bind /run/bpffs /sys/fs/bpf这样就既保留了原来的挂载又让 libxdp 能在它期望的路径上找到文件系统。当然如果上层工具支持通过参数指定 bpffs 路径优先用参数比 bind 挂载更清爽。4. 修复方案从临时救火到持久化配置4.1 临时救急把缺失的挂载补上如果排查下来确实是当前 namespace 里没有挂载最简单的修复就是手动挂载sudo mkdir -p /sys/fs/bpf sudo mount -t bpf bpf /sys/fs/bpf sudo mountpoint /sys/fs/bpf这个命令里-t bpf指定文件系统类型bpf作为设备名只是一个占位符写none也行。如果你已经看到挂载点存在但里面是空的说明它已经被挂载过了这步可以跳过。要是 mount 报already mounted说明有重复挂载先检查再处理别硬挂。临时挂载重启后会失效只适合用来做验证和救急。我实际部署时从来不会把这一步写进一个裸脚本里当长期方案它只能证明问题确实在挂载上。4.2 开机自动挂载fstab 是最不容忘的方案要让 bpffs 在系统启动后自动出现在/sys/fs/bpf最省心的方式是写进/etc/fstabnone /sys/fs/bpf bpf rw,relatime 0 0写入后先执行sudo systemctl daemon-reload sudo mount -a确认没有语法问题再重启验证一次。千万别以为 fstab 只对普通磁盘有效特殊文件系统也支持只要内核有对应驱动mount 阶段会自动处理。如果你更习惯 systemd 的方式也可以创建一个/etc/systemd/system/sys-fs-bpf.mount单元原理是一样的只是多一个可管理状态。对这个需求来说fstab 已经足够简单可靠我一般不推荐过度设计。4.3 容器和初始化脚本场景的推荐做法容器场景前面已经说过 bind mount。如果你是在初始化脚本/CI 环境里遇到这个问题我建议把检查挂载的动作放到所有 XDP 操作之前而不是在每个包里各自 mount。一个简单的自检片段if ! grep -qs /sys/fs/bpf /proc/mounts; then sudo mkdir -p /sys/fs/bpf sudo mount -t bpf bpf /sys/fs/bpf fi这段可以放在脚本最开始。注意脚本运行时的 mount namespace 就是后续所有子进程的起点所以在这里挂载一次后续命令都能看到。4.4 权限层面给指定用户可写权限排查到最后如果挂载没问题但普通用户运行 xdp-loader 依然失败可以看看是不是目录权限。bpffs 的默认 owner 是 root权限通常是 700。如果要让某个系统账号管理 XDP 程序可以sudo chown root:bpftools /sys/fs/bpf sudo chmod grwx /sys/fs/bpf把bpftools换成实际用户组。这种改法在生产环境要谨慎毕竟 bpffs 里 pin 着内核对象不应该让无关用户随便改。更好的办法是让服务通过 systemd 以指定用户运行同时只把/sys/fs/bpf/你的子目录授权给那个用户别把整个挂载点放开。5. 验证与自检确认 libxdp 这回是真的找到了5.1 三条命令快速确认环境 OK修完之后不要直接跑业务先用几条命令验证一下mountpoint /sys/fs/bpf cat /proc/self/mountinfo | grep bpf bpftool map showmountpoint输出挂载点状态mountinfo里有bpf类型挂载bpftool map show能列出哪怕一个 map都说明当前 namespace 环境是通的。如果bpftool没装用xdp-loader status替代也可以。5.2 真正让 libxdp 初始化一次环境就绪后直接跑一次 xdp-loader 的状态查询sudo xdp-loader status如果输出的是接口和插槽信息而不是No bpffs found说明 libxdp 已经成功找到 bpffs。如果想更进一步找个最小的 XDP 程序实际加载一个试试sudo xdp-loader load eth0 xdp_prog.o sudo xdp-loader unload eth0 --all如果加载阶段又出现别的报错那就可能是程序本身、BTF 或者驱动不支持的问题已经和 bpffs 无关了。这时候去看dmesg通常能找到更底层的信息。5.3 我见过的几个常见误判第一把 debugfs 当成 bpffs。/sys/kernel/debug是 debugfs不是 BPF 文件系统bpffs 要单独挂载。第二以为重启后挂载还在。临时 mount 重启就丢必须写 fstab 或 systemd unit。第三以为在宿主机 mount 完容器里就一定可见。实际上容器创建时如果不带 bind mount仍然看不到。第四以为报错就是内核不支持 XDP。先看清楚报错前缀No bpffs found和BPF syscall failed是两码事。5.4 沉淀成一套自检脚本我把这套检查写成了一个参考脚本放在所有 XDP 部署流程的最前面#!/usr/bin/env bash set -euo pipefail if ! grep -qs /sys/fs/bpf /proc/mounts; then echo bpffs not mounted, trying to mount... sudo mkdir -p /sys/fs/bpf sudo mount -t bpf bpf /sys/fs/bpf fi if [ ! -r /sys/fs/bpf ] || [ ! -w /sys/fs/bpf ]; then echo bpffs not accessible by current user exit 1 fi sudo -n xdp-loader status /dev/null 21 if [ $? -ne 0 ]; then echo xdp-loader cannot initialize libxdp exit 1 fi echo bpffs and libxdp are ready这段脚本不是万能的但它能在一开始就把挂载缺失和命名空间不匹配这类最常见的坑挡在门外。实际跑的时候如果脚本输出bpffs not accessible大概率就是权限或 SELinux 的问题再去针对性处理即可。最后再分享一个我自己的习惯现在遇到libxdp: No bpffs found at /sys/fs/bpf我第一句话会问对方你是在宿主机上跑还是在容器/systemd 服务里跑这个问题基本能定位一半问题。如果是在容器里就直接看有没有 bind/sys/fs/bpf如果是在 systemd 服务里就看 PrivateMounts 之类是不是开了如果是宿主机裸跑才去检查 fstab 和内核配置。把排查顺序理顺之后这个报错就不再神秘了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑