hash结构管理头及键值对结构体
/* Vector header for hash tables. */typedef struct hash_header{ /* hash结构中当前已存储d的元素个数*/ uword elts; /*Bit 1和bit2 表是否支持动态扩容调整哈希*/ u32 flags; /* Set if user does not want table to auto-resize when sufficiently full. */#define HASH_FLAG_NO_AUTO_GROW (1 << 0) /* Set if user does not want table to auto-resize when sufficiently empty. */#define HASH_FLAG_NO_AUTO_SHRINK (1 << 1) /* Set when hash_next is in the process of iterating through this hash table. */#define HASH_FLAG_HASH_NEXT_IN_PROGRESS (1 << 2) /*占有sizeof(hash_pair_t) 空间大小的个数的2的次幂数: h->log2_pair_size = max_log2(x) 比如x=8 = 2的3次方(_size = 3); x=9 :2的3次方不够(_size=3+1) 为0时,说明当前不存储value,只存储key。*/ u32 log2_pair_size; /* 计算散列键的函数指针。哈希函数是哈希表素数大小的模等于(vec_len (v))。*/ hash_key_sum_function_t *key_sum; /* Special values for key_sum "function". */#define KEY_FUNC_NONE (0) /*< sum = key */#define KEY_FUNC_POINTER_UWORD (1) /*< sum = *(uword *) key */#define KEY_FUNC_POINTER_U32 (2) /*< sum = *(u32 *) key */#define KEY_FUNC_STRING (3) /*< sum = string_key_sum, etc. */#define KEY_FUNC_MEM (4) /* /* key比较函数,不同的key类型对比不同的比较函数 */ hash_key_equal_function_t *key_equal; /* 用户数据的勾子函数,在mhash结构中使用Hook for user's data. Used to parameterize sum/equal functions. */ any user; /* Format a (k,v) pair */ format_function_t *format_pair; /* 格式化输出函数 的参数*/ void *format_pair_arg; /* 哈希函数获得的键值的bitmap位2层意思: * 0是:是当前未存储数据或者当前已经存储数据但有hash冲突 * 1:当前键值对应位置已存储数据*/ uword is_user[0];} hash_t;/* 键值对结构体 */typedef struct{ /*默认key是8字节*/ uword key; /* 数值. Length is 2^log2_pair_size - 1. */ uword value[0];} hash_pair_t;
通过上面的键值对结构,可以看出key是uword类型,最多存储8个字节。所以这里key一般存储的是key的指针。
hash函数内存分布
hash结构分为了3部分:hash 头结构、hash元素bitmap、hash键值对区域;
具体内存分布图如下,通过内存分布此hash是一个数组方式存储。
hash函数冲突解决及hash表扩容问题。
hash哈希实现必须要考虑的2个问题:哈希冲突解决方式及hash表扩容问题。
vpp 的hash在解决冲突时存在2中情况的:
hash表值存储key,不记录value时。
hash结构体中log2_pair_size= 0时,表述只存储key。 存储的是指针hash_pair_t*,变成vec_head_t结构存储。如下图所示:
hash表存储时,
hash结构体中log2_pair_size 非0时,存储的是hash_pair_indirect_t结构。使用alloc_len保存hash_pair_t结构数量。
增加的2中结构体,如下:
/* 哈希间接存储结构:当log2_pair_size >0 时使用*/typedef struct{ /* pair vector */ hash_pair_t *pairs; /* padding */ u8 pad[sizeof (uword) - sizeof (hash_pair_t *)]; /* allocated length */ uword alloc_len;}hash_pair_indirect_t;/* Direct / Indirect pair union */typedef union{ hash_pair_t direct; hash_pair_indirect_t indirect;} hash_pair_union_t;
hash结构的创建和操作函数
hash函数创建都是调用hash_create2((elts),0,(value_bytes),
(hash_key_sum_function_t *) KEY_FUNC_NONE,
(hash_key_equal_function_t *) KEY_FUNC_NONE,
0,0)来实现的,只是hash函数及hash比较函数不同。
总结
hash函数底层实现也依赖于vec结构。这里需要注意hash函数是不支持多线程的。所以vpp在使用一般用来存储配置表项,都是在main核中完成的。在配置下发时,会启动同步机制将worker线程锁住。所以不存在多线程并发的问题。存储量比较小时,会选择使用hash结构。当Key的大小大于8字节时,需要额外内存(比如pool结构)保存key的内容,以便于对管理hash数据。