【DPDK】原子操作-rte_atomic32_cmpset解析

rte_atomic32_cmpset(volatile uint32_t *dst, uint32_t exp, uint32_t src)称为CAS(compare and set)操作,是DPDK无锁队列实现的关键函数,代码如下:

static inline int
rte_atomic32_cmpset(volatile uint32_t *dst, uint32_t exp, uint32_t src)
{undefined
    uint8_t res;

    asm volatile(
        MPLOCKED
        "cmpxchgl %[src], %[dst];"
        "sete %[res];"
        : [res] "=a" (res),     /* output */
        [dst] "=m" (*dst)
        : [src] "r" (src),      /* input */
        "a" (exp),
        "m" (*dst)
        : "memory");            /* no-clobber list */
    return res;
}

输入操作数为*dst(memory内存)、exp(存放于eax寄存器)、src(任意可用通用寄存器)

CAS有3个操作数,内存值*dst,旧的预期值exp,要修改的新值src, 当且仅当预期值exp和内存值*dst相同时,将内存值*dst修改为src,否则什么都不做。

这个函数是如何保证CAS原子操作的呢?

MPLOCKED前缀就是锁总线,该前缀后面的指令编译成机器码后就可以使CPU在执行这条指令的时候把#HLOCK pin的电位拉低,持续到这条指令结束时放开,从而把总线锁住,这样别的CPU就暂时不能通过该总线访问内存了,确保同一时间只有一个CPU线程能操作这块内存。

cmpxchgl指令就是被lock住总线的指令,用于比较并交换操作数,这个指令是原子的。cmpxchgl比较eax(也就是exp)与 *dst的值,如果相等,那么将src的值赋值给*dst,同时标志寄存器ZF位置1;否则,将*dst的值赋值给eax,并且将标志寄存器ZF位置0。

写完之后,通过cache一致性协议保证所有其他CPU看到同一块实际内存值的更新

sete,如果标志寄存器ZF位为1,那么设置res为1否则设置为0

:”memory” 是优化屏障,防止编译器优化有顺序要求的代码

目标内存dst必须是volatile 修饰的,编译器每次遇到这个变量都从内存读值,而不从CPU自己的缓存或寄存器读值。

"=a"指定output operand应遵守的约束(constraint),res为存放指令结果的变量

“a”表示先将命令执行结果输出至eax寄存器,然后再由eax更新位于内存中的res

      如果这时候eax已经被使用,那怎么办?
      其实很简单:因为GCC 知道eax 已经被使用,它在这段汇编代码
      的起始处插入一条语句pushl %eax,将eax 内容保存到堆栈,然
      后在这段代码结束处再增加一条语句popl %eax,恢复eax的内容

"=m"表示不通过寄存器中转,而是直接操作内存

 "r" 将输入变量放入通用寄存器,也就是eax,ebx,ecx,edx,esi,edi中的一个

volatile + lock +  cmpxchgl指令 + 缓存一致性模型 + 优化屏障 = 原语CAS

`rte_hash_create`是DPDK (Data Plane Development Kit) 提供的一个函数,用于在用户空间内快速实现高效的哈希表。它允许开发者直接操作网络数据包的数据字段,提高网络应用的性能,尤其是在高性能网络设备和交换机上。 使用`rte_hash_create`的主要步骤包括以下几个关键部分: 1. **初始化**: 包括包含`rte_hash`结构体的实例化,并配置相关的选项,如哈希算法、查找模式(单播或多播)、最大条目数等。 ```c rte_hash_conf hash_conf; memset(&hash_conf, 0, sizeof(hash_conf)); hash_conf.key_size = key_length; // 键的长度 hash_conf.entry_size = entry_size; // 条目的总大小 hash_conf.socket_id = rte_socket_id(); // 使用当前socket ret = rte_hash_create(name, &hash_conf); ``` 2. **设置哈希函数**: 通过`rte_hash_add_function()`函数指定具体的哈希函数,例如使用线性探测哈希或MurmurHash。 3. **添加条目**: 使用`rte_hash_insert_entry()`将键值对添加到哈希表中。如果哈希冲突,可以有多种处理策略,比如使用开放寻址法或者链地址法。 ```c rte_hash_key_t key = { .data = key_data }; // 哈希键 void *value = ...; // 哈希值 ret = rte_hash_insert_entry(hash, &key, value); ``` 4. **查找与删除**: 可以使用`rte_hash_find_entry()`查询特定键对应的值,或`rte_hash_remove_entry()`移除条目。 5. **销毁哈希表**: 在不再使用时,记得通过`rte_hash_free()`释放资源。 **相关问题--**: 1. `rte_hash_create`支持哪些常见的哈希函数? 2. 怎样在`rte_hash_create`中配置查找模式? 3. 如何在`rte_hash`中处理键冲突?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值