参考资料
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(¶ms);
/* 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_src、ip_dst等均一致),它们的填充字节也可能不同,导致:rte_jhash计算出的哈希值不同(因为输入的二进制内容不同)。- 哈希表查找时,会认为这两个键是不同的(即使 5 元组相同),最终导致 "添加的键无法被查到"。