vppinfra -- mhash介绍

2023-03-07 16:58:53 浏览数 (1)

今天来介绍一下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结构体介绍
代码语言:javascript复制
/* 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的地址。

0 人点赞