dpdk-3.hash表CURD

参考资料

dpdk第三课------hash表增删改查的使用
Hash Library
rte_hash.h

源码

c 复制代码
// 五元组
struct flow_key {
	uint32_t ip_src; 	//src address
	uint32_t ip_dst;	//dst address
	uint16_t port_src;	//src port
	uint16_t port_dst;	//dst port
	uint8_t proto;		//protocol
}__rte_packed;
// _rte_packed 强制结构体紧凑排列,无内存空洞

// 初始化
static void
init_test_flow_key(struct flow_key *key)
{
	key->ip_src = RTE_IPV4(0x03, 0x02, 0x01, 0x00);
	key->ip_dst = RTE_IPV4(0x07, 0x06, 0x05, 0x04);
	key->port_src = 0x0908;
	key->port_dst = 0x0b0a;
	key->proto = 15;
}

/* 哈希表参数配置 */
struct rte_hash_parameters params = {
    .name = "flow_table",          // 哈希表名称(用于调试和区分多个表)
    .entries = 64,                 // 哈希表最大容量(64个条目)
    .key_len = sizeof(struct flow_key),  // 密钥长度(5元组的字节数:4+4+2+2+1=13字节)
    .hash_func = rte_jhash,        // 哈希函数(DPDK内置的Jenkins哈希,速度快)
    .hash_func_init_val = 0,       // 哈希函数初始值(用于自定义哈希结果)
    .socket_id = 0,                // 内存分配的NUMA节点(0表示第一个节点)
};

int main(int argc, char **argv)
{
    int ret;
    struct rte_hash *hash_table = NULL;
    /* init EAL */
    ret = rte_eal_init(argc, argv);
    
    // 初始化key
    struct flow_key firstKey;
    init_test_flow_key(&firstKey);
    
    /* 创建hash表 */
    hash_table = rte_hash_create(&params);
    
    /* hash表插入key */
    int pos = rte_hash_add_key(hash_table, &firstKey);
    if (pos < 0) {
        printf("ERROR: Cannot add key to hash table: %s\n", strerror(-pos));
        goto cleanup;
    }

    /* hash表查询key */
    pos = rte_hash_lookup(hash_table, &firstKey);
    if (pos < 0) {
        printf("ERROR: Cannot lookup key in hash table: %s\n", strerror(-pos));
        goto cleanup;
    }

    /* 初始化相同的key */
    struct flow_key secondKey;
    init_test_flow_key(&secondKey);
    
    // 查询key
    pos = rte_hash_lookup(hash_table, &secondKey);
    if (pos < 0) {
        printf("ERROR: Cannot lookup session key in hash table: %s\n", strerror(-pos));
        /* calculate the hash of the session key */
        uint32_t hashNow = rte_jhash(&secondKey, sizeof(struct flow_key), 0);
        printf("INFO: Hash of session key: %u\n", hashNow);
        uint32_t hashBefore = rte_jhash(&firstKey, sizeof(struct flow_key), 0);
        printf("INFO: Hash of original key: %u\n", hashBefore);
        goto cleanup;
    }

    /* hash表删除key */
    pos = rte_hash_del_key(hash_table, &firstKey);
    if (pos < 0) {
        printf("ERROR: Cannot delete key from hash table: %s\n", strerror(-pos));
        goto cleanup;
    }

    printf("INFO: Hash table operations completed successfully\n");

cleanup:
    /* clean up resources */
    if (hash_table != NULL) {
        rte_hash_free(hash_table);
        printf("INFO: Hash table successfully freed\n");
    }
    rte_eal_cleanup();
    return (hash_table == NULL) ? -1 : 0;
}

哈希表参数配置

c 复制代码
struct rte_hash_parameters params = {
    .name = "flow_table",          // 哈希表名称(用于调试和区分多个表)
    .entries = 64,                 // 哈希表最大容量(64个条目)
    .key_len = sizeof(struct flow_key),  // 密钥长度(5元组的字节数:4+4+2+2+1=13字节)
    .hash_func = rte_jhash,        // 哈希函数(DPDK内置的Jenkins哈希,速度快)
    .hash_func_init_val = 0,       // 哈希函数初始值(用于自定义哈希结果)
    .socket_id = 0,                // 内存分配的NUMA节点(0表示第一个节点)
};

哈希表核心操作

操作 函数 功能与返回值说明
创建哈希表 rte_hash_create params配置创建哈希表,返回struct rte_hash*指针;失败返回 NULL。
添加密钥 rte_hash_add_key 将 5 元组密钥加入哈希表,返回 "密钥在表中的索引(pos≥0)";失败返回负数(如 - ENOSPC)。
查找密钥 rte_hash_lookup 查找密钥是否存在,返回 "索引(pos≥0)";不存在返回 - ENOENT。
删除密钥 rte_hash_del_key 从表中删除密钥,返回 "被删除密钥的索引(pos≥0)";失败返回负数。
释放哈希表 rte_hash_free 释放哈希表占用的内存资源,无返回值。

如果没有 _rte_packed

编译器默认会为结构体成员添加填充字节,目的是让成员地址满足 "对齐要求"(如 uint32_t 通常需要 4 字节对齐,uint16_t 需要 2 字节对齐),以提升 CPU 访问效率。但在哈希计算等场景中,填充字节会破坏数据的 "完整性"。

如果存在填充字节:

  • 填充字节的内容是未定义的(可能是随机值、内存残留值等)。
  • 即使两个 flow_key 的 5 元组成员完全相同(ip_srcip_dst 等均一致),它们的填充字节也可能不同,导致:
    • rte_jhash 计算出的哈希值不同(因为输入的二进制内容不同)。
    • 哈希表查找时,会认为这两个键是不同的(即使 5 元组相同),最终导致 "添加的键无法被查到"。
相关推荐
Dragon Wu26 分钟前
Spring Security Oauth2.1 授权码模式实现前后端分离的方案
java·spring boot·后端·spring cloud·springboot·springcloud
一个有梦有戏的人1 小时前
Python3基础:进阶基础,筑牢编程底层能力
后端·python
爬山算法1 小时前
Hibernate(88)如何在负载测试中使用Hibernate?
java·后端·hibernate
独断万古他化1 小时前
【Spring 原理】Bean 的作用域与生命周期
java·后端·spring
我爱加班、、2 小时前
Websocket能携带token过去后端吗
前端·后端·websocket
一 乐2 小时前
校园二手交易|基于springboot + vue校园二手交易系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端
80530单词突击赢2 小时前
SpringBoot整合SpringMVC全解析
java·spring boot·后端
hdsoft_huge2 小时前
1panel面板中部署SpringBoot和Vue前后端分离系统 【图文教程】
vue.js·spring boot·后端
lekami_兰3 小时前
RabbitMQ 延迟队列实现指南:两种方案手把手教你搞定
后端·rabbitmq·延迟队列
程序员泠零澪回家种桔子3 小时前
Sentinel核心能力解析:限流与集群方案
后端·架构·sentinel