Ris*_*hab 3 bpf ebpf xdp-bpf xdp-ebpf libbpf
我可以从文档中确认,如果在 HASH_MAP 上完成,bpf_map_update_elem 是一个原子操作。来源(https://man7.org/linux/man-pages/man2/bpf.2.html)。[引用:map_update_elem() 以原子方式替换现有元素]
我的问题是2倍。
如果元素不存在怎么办,map_update_elem 仍然是原子的吗?
XDP 操作 bpf_map_delete_elem 线程在用户空间程序中安全吗?
该映射是 HASH_MAP。
原子操作、竞争条件和线程安全在 eBPF 中有点复杂,所以我将做出一个广泛的回答,因为很难从你的问题判断你的目标是什么。
是的,bpf_map_update_elem通过系统调用的命令和辅助函数都会“自动”更新映射,在这种情况下,这意味着如果我们从值“A”转到值“B”,程序总是会看到“A”或“B” ' 不是两者的某种组合(例如“B”的第一个字节和“A”的最后一个字节)。对于所有地图类型都是如此。这对于所有映射修改系统调用命令(包括 )都适用bpf_map_delete_elem。
然而,这并不会使竞争条件成为不可能,因为地图的值可能在 amap_lookup_elem和更新它的那一刻之间发生了变化。
还需要记住的是,map_lookup_elem系统调用命令(用户空间)与辅助函数(内核空间)的工作方式不同。系统调用将始终返回不可变的数据副本。但是辅助函数将返回一个指向内核内存中存储映射值的位置的指针,您可以通过这种方式直接更新映射值,而无需使用辅助函数map_update_elem。这就是为什么您经常看到哈希映射的使用方式如下:
value = bpf_map_lookup_elem(&hash_map, &key);
if (value) {
__sync_fetch_and_add(&value->packets, 1);
__sync_fetch_and_add(&value->bytes, skb->len);
} else {
struct pair val = {1, skb->len};
bpf_map_update_elem(&hash_map, &key, &val, BPF_ANY);
}
Run Code Online (Sandbox Code Playgroud)
请注意,在此示例中, ,__sync_fetch_and_add用于更新部分地图值。我们需要这样做,因为更新它就像value->packets++;或value->packets += 1会导致竞争条件。它__sync_fetch_and_add发出一条原子 CPU 指令,在本例中,该指令在一条指令中完成所有的提取、添加和写回操作。
另外,在这个例子中,两个结构体字段被原子更新,但不是一起更新,仍然有可能已经packets增加但bytes尚未增加。如果你想避免这种情况,你需要使用自旋锁(使用bpf_spin_lock和bpf_spin_unlock helpers)。
完全回避该问题的另一种方法是使用_PER_CPU地图的变体,在其中权衡拥塞/速度和内存使用。
| 归档时间: |
|
| 查看次数: |
915 次 |
| 最近记录: |