BPF bpf_spin_lock 使用排错指南

本文介绍了BPF自旋锁的使用方法,包括其在多核竞争情况下的作用原理及如何应用于BPF map元素的访问控制。通过示例代码详细展示了自旋锁的加锁与解锁过程,并解释了BPF类型格式注释的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

使用BPF时,多核之间难免会有竞争,为了应对这种情况,BPF引入了BPF自旋锁(bpf_spin_lock)的概念,它允许对map元素进行操作时锁定对map元素的访问。自旋锁仅可存储于array、hash和cgroup类型的map中。

// 信号量
// /usr/include/linux
struct bpf_spin_lock {
	__u32	val;
};

// 内核
// 加锁+解锁
// tools/testing/selftests/bpf/bpf_helpers.h
static void (*bpf_spin_lock)(struct bpf_spin_lock *lock) =
	(void *) BPF_FUNC_spin_lock;
static void (*bpf_spin_unlock)(struct bpf_spin_lock *lock) =
	(void *) BPF_FUNC_spin_unlock;

这里复制下书上的事例。这个访问控制,精度比较细。对每一个元素使用了自旋锁。另外map必须用BPF类型格式(BPF Type Format,BTF)注释,这样verifier就知道如何解释这个结构。类型格式通过向二进制对象添加调试信息,使内核和其他工具对BPF数据结构有了更丰富的理解。

struct concurrent_element {
    struct bpf_spin_lock semaphore;
    int count;
}

struct bpf_map_def SEC("maps") concurrent_map = {
    .type = BPF_MAP_TYPE_HASH,
    .key_size = sizeof(int),
    .value_size = sizeof(struct concurrent_element),
    .max_entries = 100,
};

// 这个宏很重要,如果没有则在verifier时会报错
BPF_ANNOTATE_KV_PAIR(concurrent_map, int, struct concurrent_element);

int bpf_program(struct pt_regs *ctx) {
	int key = 0;
    struct concurrent_element init_value = {};
    struct concurrent_element *read_value;
    bpf_map_create_elem(&concurrent_map, &key, &init_value, BPF_NOEXIST);
    read_value = bpf_map_lookup_elem(&concurrent_map, &key);
    bpf_spin_lock(&read_value->semaphore);
    read_value->count += 100;
    bpf_spin_unlock(&read_value->semaphore);
}

BPF_ANNOTATE_KV_PAIR()宏很重要,作用是为特定maps添加类型解释,就是使用它为map进行BPF类型格式(BPF Type Format,BTF)注释的。如果不执行这个宏,就会报has to have BTF in order to use bpf spin lock的错误。
在这里插入图片描述
当程序或map加载之后可以通过下面命令查看其类型信息:

bpftool map dump id 386
bpftool prog show id 72
bpftool btf show
bpftool btf dump id 60 format c

重点来了

我使用的libbpf中并没有实现BPF_ANNOTATE_KV_PAIR()这个宏的定义,如果你也遇到同样的情况,可以自己手动把下面的宏定义加到代码或头文件中。

#define BPF_ANNOTATE_KV_PAIR(name, type_key, type_val)      \
        struct ____btf_map_##name {             \
                    type_key key;                   \
                    type_val value;                 \
                };                          \
        struct ____btf_map_##name               \
            __attribute__ ((section(".maps." #name), used))     \
            ____btf_map_##name = { }

重新编译加载代码后,has to have BTF in order to use bpf spin lock的错误依然存在。使用objdump -h xdp_prog.o命令,可以看到文件中并没有.BTF段。
在这里插入图片描述
这个问题的解决方法是在用clang编译时加上-g选项。重新编译后的结果如下图,可以看到.BTF段。
在这里插入图片描述
重启程序,verifier时就不再报错了。

bpf_timer_init是一个BPF内核函数,用于初始化一个定时器,以便在将来的时间间隔内执行一个BPF程序。 以下是bpf_timer_init的使用示例: ```c #include <linux/bpf.h> #include <bpf/bpf_helpers.h> struct bpf_map_def SEC("maps") my_map = { .type = BPF_MAP_TYPE_HASH, .key_size = sizeof(int), .value_size = sizeof(long), .max_entries = 1024, }; SEC("timer") int my_timer(struct bpf_timer *timer) { int key = 0; long value = 0; bpf_map_lookup_elem(&my_map, &key, &value); value++; bpf_map_update_elem(&my_map, &key, &value, BPF_ANY); return 0; } char _license[] SEC("license") = "GPL"; // 初始化定时器 struct bpf_timer mytimer SEC("maps") = { .interval = 1000, // 每1秒执行一次 .handler = my_timer, }; int _version SEC("version") = 0xFFFFFFFE; ``` 在上面的示例代码中,我们定义了一个名为“my_timer”的BPF程序,它会在每1秒钟执行一次,并对一个名为“my_map”的哈希表进行更新。我们还定义了一个名为“mytimer”的定时器,它被初始化为每1秒钟执行一次“my_timer”程序。最后,我们将BPF程序和定时器都加载到内核中。 请注意,定时器必须以“timer”作为BPF程序的Section名称,以便内核能够正确识别它。此外,我们还需要将定时器定义为一个特殊的BPF_MAP_TYPE_PERF_EVENT_ARRAY映射类型,以便BPF程序可以与定时器进行交互。定时器的初始化可以在BPF程序加载之前完成,以确保在程序执行之前定时器已经启动。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

高晓伟_Steven

相逢即是有缘,动力源于金钱。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值