今天来介绍一下mhash结构及简单使用。mhash底层是hash结构。并且还使用了heap结构。不清楚heap的结构的可以看一下上一节heap介绍
这里有一个细节就是函数初始化名称不同,mhash是init,而普通hash是create。个人理解就是告诉我们mhash的结构是有使用者申请并维护的。
代码语言:javascript复制void
mhash_init (mhash_t * h, uword n_value_bytes, uword n_key_bytes)
void *_hash_create (uword elts, hash_t * h);
- mhash结构体介绍
/* Hash table plus vector of keys. */
typedef struct
{
/* Vector or heap used to store keys. Hash table stores keys as byte
offsets into this vector. */
u8 *key_vector_or_heap;
/* Byte offsets of free keys in vector (used to store free keys when
n_key_bytes > 1). */
u32 *key_vector_free_indices;
u8 **key_tmps;
/* Possibly fixed size of key.
0 means keys are vectors of u8's.
1 means keys are null terminated c strings. */
#define MHASH_VEC_STRING_KEY 0
#define MHASH_C_STRING_KEY 1
u32 n_key_bytes;
/* Seed value for Jenkins hash. */
u32 hash_seed;
/* Hash table mapping key -> value. */
uword *hash;
/* Format function for keys. */
format_function_t *format_key;
} mhash_t;
key_vector_or_heap :
存储Key内容的内存区域,可以是vector 或heap。hash表中Key只是保存了key首地址基于key_vector_or_heap的字节偏移量。
代码语言:javascript复制always_inline void *
mhash_key_to_mem (mhash_t * h, uword key)
{
if (key == ~0) /*主要用于hash unset 和 get,需要key的临时区*/
{
u8 *key_tmp;
int my_cpu = os_get_thread_index ();
vec_validate (h->key_tmps, my_cpu);
key_tmp = h->key_tmps[my_cpu];
return key_tmp;
}
/* 从这里可以看出使用的偏移量*/
return vec_elt_at_index (h->key_vector_or_heap, key);
}
key_vector_free_indices:
当n_key_bytes > 1时,存储已经释放的key。
n_key_bytes :
0: 表述存储的是字符串。
1: 表示存储的vector结构的字符串。
其他还支持2 --64字节大小的key。
u8 **key_tmps;
二级指针,这个key临时存储区。key_tmps[thread_index]指向vector结构的头。 这个在mhash_free时没有释放,会导致少量内存泄漏。
这个设计应该是为了多线程mhash_get操作线程安全。但是mhash本身是不支持多线程的。所以mhash set函数一般只能在main核执行,worker核只是用来取value。有个疑问:mhash_get的时候,key的内容我们已经组装好。为什么还需要把key内容存储到key_tmps[cpu]中?
u32 hash_seed:hash 种子,没有看到有赋值的地方,预留的接口把,结算hash值的时候使用。
- mhash key存储说明
当n_key_bytes > 1时
当n_key_bytes<= 1时,使用heap来实现,因为heap本身就是一个可变长的内存池,不需要外部管理free区。
下面mhash_set的代码片段了解其使用。
代码语言:javascript复制 typedef struct
{
u32 heap_handle;
/* Must coincide with vec_header. */
vec_header_t vec;
} mhash_string_key_t;
mhash_string_key_t *sk;
uword handle;
/*i 是offset*/
i =
heap_alloc (h->key_vector_or_heap, n_key_bytes sizeof (sk[0]),
handle);
sk = (void *) (h->key_vector_or_heap i);
/*需要报错其hanle,释放时使用。*/
sk->heap_handle = handle;
sk->vec.len = n_key_bytes;
/*拷贝key内容*/
clib_memcpy_fast (sk->vec.vector_data, key, n_key_bytes)
/* Advance key past vector header. key的偏移量*/
i = sizeof (sk[0]);
在mhash_string_key_t结构中vec_header_t vec;必须存储vec结构,应该当MHASH_VEC_STRING_KEY类型时需要用到。
- mhash bug。 当n_key_bytes> 1时,mhash_set两次,会出现踩内容问题。 https://jira.fd.io/browse/VPP-1844
- 总结 mhash和hash的不同点,就是mhash会存储key的内容,不需要使用者申请额外内存存储key内容。mhash的使用场景比较少。mash 使用了h->user字段来存储mhash的地址。