You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在libbpf中使用BPF_MAP_TYPE_HASH_OF_MAPS实现键控直方图?

正确使用BPF_MAP_TYPE_HASH_OF_MAPS实现键控直方图

问题背景

你当前在libbpf中实现bpftrace风格的直方图,已完成单级哈希表(LatencyGroup -> Frequency),现在要升级为两级键控结构(String -> LatencyGroup -> Frequency),尝试使用BPF_MAP_TYPE_HASH_OF_MAPS时遇到两个核心问题:

  1. libbpf-rs找不到bpf_map_create函数
  2. 引入<bpf/bpf.h>头文件后触发类型重定义编译错误

核心错误分析

  1. bpf_map_create是用户态API:该函数只能在用户空间调用,不能在BPF内核程序中使用,你之前在内核代码中尝试创建内层Map的逻辑完全错误。
  2. 头文件冲突:vmlinux.h已经包含了完整的内核类型定义,再引入用户态的<bpf/bpf.h>会导致内核类型与用户态类型重复定义,必须避免在BPF内核程序中包含该头文件。

正确实现方案

1. BPF内核程序代码修改

只保留内核侧必要头文件,定义外层哈希嵌套Map与内层Map模板,内核程序仅负责操作已关联的内层Map:

#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/usdt.bpf.h>

// 内层直方图哈希表模板:bin值 -> 计数
struct InnerLatencyMap {
    __uint(type, BPF_MAP_TYPE_HASH);
    __uint(max_entries, 1024);
    __type(key, u64);
    __type(value, u64);
};

// 外层嵌套哈希表:进程名(comm) -> 内层直方图
struct {
    __uint(type, BPF_MAP_TYPE_HASH_OF_MAPS);
    __uint(max_entries, 1024);
    __type(key, char[16]); // 兼容Linux默认16字节comm长度(含终止符)
    __array(values, struct InnerLatencyMap); // 关联内层Map模板
} latency_map SEC(".maps");

void increment(char *comm, u64 bin) {
    // 查找当前进程名对应的内层直方图Map
    struct bpf_map *inner_map = bpf_map_lookup_elem(&latency_map, comm);
    if (!inner_map) {
        // 内层Map需由用户态预先创建并关联,内核无法动态创建
        return;
    }

    // 更新对应bin的计数
    u64 *count = bpf_map_lookup_elem(inner_map, &bin);
    if (count) {
        (*count)++;
    } else {
        u64 init_count = 1;
        bpf_map_update_elem(inner_map, &bin, &init_count, BPF_NOEXIST);
    }
}

2. 用户态(libbpf-rs)代码补充

内层Map的创建与关联必须在用户态完成,示例代码如下:

use libbpf_rs::{Map, MapFlags, Object};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 加载BPF对象
    let mut bpf_object = Object::from_file("your_bpf_program.o")?;
    bpf_object.load()?;
    bpf_object.attach()?;

    // 获取外层嵌套Map
    let mut latency_map = bpf_object.map_mut("latency_map")?;

    // 示例:为进程名"python"创建并关联内层直方图Map
    let comm = "python\0".as_bytes(); // 必须以NULL终止符结尾
    let inner_map = Map::new(
        libbpf_rs::MapType::Hash,
        None,
        std::mem::size_of::<u64>(), // 内层Map键大小(bin值)
        std::mem::size_of::<u64>(), // 内层Map值大小(计数)
        1024, // 内层Map最大条目数
        MapFlags::empty(),
    )?;

    // 将内层Map关联到外层Map的对应进程名键
    latency_map.update(comm, &inner_map.fd(), MapFlags::NOEXIST)?;

    // 后续可通过perf event或轮询处理内核数据,此处省略业务逻辑
    loop {
        std::thread::sleep(std::time::Duration::from_secs(1));
    }
}

关键注意事项

  • 内层Map的生命周期:用户态需负责管理内层Map的创建与销毁,确保外层Map引用的内层Map始终有效。
  • 键类型兼容性:外层Map的键类型要与实际数据匹配(如comm用固定长度数组,避免动态内存问题)。
  • 性能优化:可提前为已知进程创建内层Map,避免内核中频繁触发lookup失败的逻辑。

内容的提问来源于stack exchange,提问作者mq7

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 11:04:54