Linux bpftool map update / lookup 的用户态 map 读写实践

1次阅读

bpftool map update 提示“invalid argument”的根本原因是用户态数据格式与内核map结构不匹配,包括value大小、key类型或对齐方式错误,如value_size定义为8却写入16字节;需用bpftool map show确认参数,并确保key/value为连续二进制块、无padding指针类map不支持直接更新。

Linux bpftool map update / lookup 的用户态 map 读写实践

bpftool map update 为什么总提示 “Invalid argument”

根本原因通常是用户态数据格式和内核 map 结构不匹配,尤其是 value 大小、key 类型或对齐方式出错。比如用 bpftool map update 往一个 BPF_MAP_TYPE_HASH 中写入 16 字节 value,但 map 定义里 value_size 是 8,就会触发这个错误。

  • 先用 bpftool map show id <id></id> 确认 map 的 key_sizevalue_sizetypemax_entries
  • key 和 value 必须是连续的二进制块,不能带结构体 padding(C 编译器可能自动加),建议用 __attribute__((packed)) 声明或手动生成字节数组
  • 如果 map value 是指针类型(如 BPF_MAP_TYPE_PERCPU_Array),bpftool map update 不支持直接写入——它只处理普通值,不处理 percpu 内存布局
  • 更新失败时,加 -d 参数看详细错误:例如 bpftool -d map update ... 会输出类似 libbpf: failed to update map: Invalid argument (22),对应 EINVAL

用 bpftool map lookup 查不到刚写进去的 key

常见于 map 类型不支持查找语义,或者 key 没按字节序/对齐要求构造。比如 BPF_MAP_TYPE_PERF_EVENT_ARRAY 根本不支持 lookup;又比如 map key 是 __be32,你却传了小端整数过去,内核比对失败。

  • bpftool map lookup 只支持 HASHARRAYLRU_HASH 等可查类型;PERF_EVENT_ARRAYPROG_ARRAYCPUMAP 都会报 Operation not supported
  • key 必须严格按定义的 key_size 补零,少一字节或多一字节都会返回 No such file or Directory
  • 如果 map 是 BPF_MAP_TYPE_ARRAY,key 是数组索引(0-based uint32),但你传了 0x0000000100000000(8 字节)进去,lookup 就会失败——必须是 4 字节
  • 查不到时别急着重试,先用 bpftool map dump 看全量内容,确认 key 是否真被写进去了

用户态读写 BPF map 的替代方案:为什么不全用 libbpf

bpftool 是调试利器,但不适合集成进长期运行的用户态程序。它每次调用都 fork + exec,开销大,且无法复用 fd、做批量操作或监听 map 变化。

  • libbpf 提供 bpf_map__update_elem()bpf_map__lookup_elem(),支持自定义 flags(如 BPF_ANY / BPF_NOEXIST)、批量更新(bpf_map__update_batch())、以及内存映射(mmap)访问 ARRAY 类型 map
  • bpftool 写入的数据,libbpf 能立刻读到——它们共享同一内核对象,fd 或 id 是互通的
  • 注意:libbpf 默认使用 ALWAYS 模式加载 map,若 map 已存在(比如由 bpftool 创建),需显式调用 bpf_obj_get() 获取 fd,再封装Struct bpf_map *
  • 对高频更新场景(如统计计数器),直接 mmap ARRAY map 比反复 syscalls 快 10x+;但 HASH map 不支持 mmap,只能 syscall

map update 后 bpf 程序读不到新值?检查 pin 路径和生命周期

很多人把 map pin 到 /sys/fs/bpf/xxx,然后用 bpftool 更新,却发现 attached 的 XDP 程序还在读旧值——其实不是没更新,而是程序 attach 时绑定的是另一个 map 实例。

  • 确保 bpf 程序加载时用的是同一个 pin 路径:比如加载时指定 --map-fd 3(fd 来自 bpf_obj_get("/sys/fs/bpf/my_map")),而不是让 libbpf 自动创建新 map
  • bpftool map list 查所有 map,对比 pinned path 和 program 所用 map 的 id 是否一致;不同 id = 不同对象
  • 如果 map 被 unpin 或进程退出导致 fd 关闭,而 bpf 程序还在运行,map 会被内核自动释放(除非有其他引用)——所以生产环境务必用 pin + 持久化路径
  • 更新后立即查不到,也可能是 bpf 程序用了 __builtin_preserve_access_index 访问结构体字段,但用户态写入时字段 offset 错了,导致 bpf 读到全零或越界值

实际用起来最麻烦的不是命令怎么敲,而是 key/value 的二进制布局和 map 生命周期管理——这两处出问题,现象往往似是而非,错误信息又藏得深。

text=ZqhQzanResources