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

BPF_MAP_TYPE_PERCPU_ARRAY未零初始化?创建后部分CPU值异常咨询

BPF_PERCPU_ARRAY 未被零初始化的问题

我尝试用BPF_MAP_TYPE_PERCPU_ARRAY统计SK_SKB钩子处接收的数据包数量,但创建映射后立即用bpftool map dump查看,发现部分CPU对应的值并未被零初始化。

映射定义如下:

struct {
  __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
  __uint(max_entries, 1);
  __type(key, int);
  __type(value, __u64);
  __uint(pinning, LIBBPF_PIN_BY_NAME);
} request_count_map SEC(".maps");

bpftool map dump输出:

$ sudo bpftool map dump id 287
[{
        "key": 0,
        "values": [{
                "cpu": 0,
                "value": 0
            },{
                "cpu": 1,
                "value": 94322157119214
            },{
                "cpu": 2,
                "value": 140730068914171
            },{
                "cpu": 3,
                "value": 94322160575280
            },{
                "cpu": 4,
                "value": 94322160592312
            },{
                "cpu": 5,
                "value": 94322160592512
            },{
                "cpu": 6,
                "value": 80
            },{
                "cpu": 7,
                "value": 0
            },{
                "cpu": 8,
                "value": 0
            },{
                "cpu": 9,
                "value": 140730068932048
            },{
                "cpu": 10,
                "value": 0
            },{
                "cpu": 11,
                "value": 0
            },{
                "cpu": 12,
                "value": 0
            },{
                "cpu": 13,
                "value": 0
            },{
                "cpu": 14,
                "value": 0
            },{
                "cpu": 15,
                "value": 0
            },{
                "cpu": 16,
                "value": 80
            },{
                "cpu": 17,
                "value": 0
            },{
                "cpu": 18,
                "value": 0
            },{
                "cpu": 19,
                "value": 140730068932048
            }
        ]
    }
]

根据内核文档和man手册的描述,数组类型的映射元素应该在创建时预分配并零初始化,但实际结果不符。

补充:我在加载时尝试手动更新映射,代码如下:

int index = 0;
__u64 value = 0;

// 获取映射文件描述符
reqcnt_map_fd = bpf_obj_get(reqcnt_map_filename);

// 更新映射值
err = bpf_map_update_elem(respcnt_map_fd, &index, &value, BPF_ANY);
if (err) {
  goto fail;
}

但执行后部分CPU的值依然不是0,这是为什么?


问题根源

你遇到的问题是用户态更新percpu数组映射时的参数错误:

对于BPF_MAP_TYPE_PERCPU_ARRAY这类percpu类型的映射,用户态调用bpf_map_update_elem时,value参数不能是单个值,而必须是一个包含所有CPU对应值的数组。内核会将数组中的每个元素依次赋值给对应CPU的存储位置。

你的代码中只传递了单个__u64 value = 0,内核会读取该变量之后的栈内存作为其他CPU的值(因为内核预期接收的是长度为CPU数量 * sizeof(__u64)的内存块),这些栈内存是未初始化的垃圾数据,所以最终部分CPU显示出随机的非零值。

解决方法

  1. 正确初始化用户态数组:
    先获取系统的CPU数量,然后创建一个对应长度的数组,将所有元素初始化为0,再传递给bpf_map_update_elem:

    #include <unistd.h>
    #include <sys/sysinfo.h>
    
    int main() {
        int index = 0;
        int cpu_count = get_nprocs_conf(); // 获取系统总CPU数
        __u64 *values = calloc(cpu_count, sizeof(__u64)); // 全部初始化为0
    
        int reqcnt_map_fd = bpf_obj_get(reqcnt_map_filename);
        int err = bpf_map_update_elem(reqcnt_map_fd, &index, values, BPF_ANY);
    
        free(values);
        if (err) {
            // 错误处理
            return -1;
        }
        return 0;
    }
    
  2. 利用BPF程序初始化(更推荐):
    不需要在用户态手动初始化,而是在BPF程序中第一次访问映射时,通过bpf_map_lookup_elem获取指针并初始化。因为percpu数组的每个CPU存储区在第一次被BPF程序访问时,内核会确保其被零初始化(文档描述的零初始化实际是针对BPF程序访问的场景,用户态直接dump未被访问过的CPU存储区可能会看到未初始化的内存):

    SEC("sk_skb")
    int sk_skb_handler(struct __sk_buff *skb) {
        __u64 *count = bpf_map_lookup_elem(&request_count_map, &(int){0});
        if (count) {
            (*count)++;
        }
        return 0;
    }
    

    当每个CPU上的BPF程序第一次执行到这段代码时,对应CPU的存储区会被自动零初始化,后续统计就是正确的。

额外说明

内核文档中提到的“零初始化”,更多是指BPF程序访问时的保证。对于用户态来说,percpu数组中未被任何CPU的BPF程序访问过的存储区,其内存可能保留着之前的垃圾数据,这是正常的内核内存分配行为。所以更可靠的方式是让BPF程序自己完成初始化,或者在用户态正确传递全CPU的零值数组。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 11:05:32