nginx之ngx_hash

nginx之ngx_hash

nginx使用了开放地址法解决哈希冲突,还有利用内存对齐使用的一些小技巧等等。

下面是关于ngx_hash.h中的一些定义:

/**
 *先看nginx里面对hash每个元素的定义:
 */
typedef struct {
    void             *value;
    u_short           len;
    u_char            name[1]; 
} ngx_hash_elt_t;

这里使用了一个技巧,使用了零长数组,当申请的内存被value和len占据相应的size后,剩余的内存对于name来说可以使用。

/**
 *nginx对ngx_hash_t的定义
 */
typedef struct { 
    ngx_hash_elt_t  **buckets;
    ngx_uint_t        size;
} ngx_hash_t;
/**
 *nginx对ngx_wildcard_t的定义
 */
typedef struct {
    ngx_hash_t        hash;
    void             *value;
} ngx_hash_wildcard_t;
/**
 *nginx对ngx_hash_key_t的定义
 */
typedef struct {
    ngx_str_t         key;
    ngx_uint_t        key_hash;
    void             *value;
} ngx_hash_key_t;
/**
 *nginx对ngx_hash_combined_t的定义
 */
typedef struct {
    ngx_hash_t            hash;
    ngx_hash_wildcard_t  *wc_head;
    ngx_hash_wildcard_t  *wc_tail;
} ngx_hash_combined_t;
/**
 *nginx对ngx_hash_init_t的定义
 */
typedef struct {
    ngx_hash_t       *hash;
    ngx_hash_key_pt   key;

    ngx_uint_t        max_size;
    ngx_uint_t        bucket_size;

    char             *name;
    ngx_pool_t       *pool;
    ngx_pool_t       *temp_pool;
} ngx_hash_init_t;
/**
 *nginx对ngx_hash_key_array_t的定义
 */
typedef struct {
    ngx_uint_t        hsize;

    ngx_pool_t       *pool;
    ngx_pool_t       *temp_pool;

    ngx_array_t       keys;
    ngx_array_t      *keys_hash;

    ngx_array_t       dns_wc_head;
    ngx_array_t      *dns_wc_head_hash;

    ngx_array_t       dns_wc_tail;
    ngx_array_t      *dns_wc_tail_hash;
} ngx_hash_keys_arrays_t;
/**
 *nginx对ngx_hash_table_elt_t的定义
 */
typedef struct {
    ngx_uint_t        hash;
    ngx_str_t         key;
    ngx_str_t         value;
    u_char           *lowcase_key;
} ngx_table_elt_t;

/**
 *一些nginx自定义的字段
 */
#define NGX_HASH_SMALL            1
#define NGX_HASH_LARGE            2

#define NGX_HASH_LARGE_ASIZE      16384
#define NGX_HASH_LARGE_HSIZE      10007

#define NGX_HASH_WILDCARD_KEY     1
#define NGX_HASH_READONLY_KEY     2

ngx_hash.c:

ngx_int_t ngx_hash_init()解析:

/**
 *对齐只需保证末尾的bit位为0即可
 *如8字节对齐保证末尾3个bit位为0,4字节对齐保证末尾2个bit为0
 *不同平台下的void*大小不一样,32位长为4个字节,64位长为8个字节
 */
#define NGX_HASH_ELT_SIZE(name)									\
(sizeof(void *) + ngx_align((name)->key.len + 2, sizeof(void *)))
//这是在ngx_config.h里面关于ngx_align()的定义
#define ngx_align(d, a)     (((d) + (a - 1)) & ~(a - 1))

NGX_HASH_ELT_SIZE(name)是nginx在使用内存对齐的一个自定义的宏,可以利用其计算出每个hash桶里面每个elt在内存对齐后所占用的内存大小。

nginx采用的是预分配的方式来决定需要多少个bucket,通过计算算出至少需要start个桶,再通过计算每个names[n].key的hash值,并计算bucket_size是否能够容纳所有的elt,如果可以就确定了size值,如果无法容纳就增大start值。

bucket_size = hinit->bucket_size - sizeof(void *);

start = nelts / (bucket_size / (2 * sizeof(void *)));
start = start ? start : 1;

nginx按照cpu的缓存大小进行内存对齐的,来提高程序运行效率。

elts = ngx_palloc(hinit->pool, len + ngx_cacheline_size);
if (elts == NULL) {
    ngx_free(test);
    return NGX_ERROR;
}

elts = ngx_align_ptr(elts, ngx_cacheline_size);

将names中的各个元素添加进hash桶里,而且最后nginx在hash桶里面设置哨兵节点,由value=NULL确定,且该哨兵节点elt只分配sizeof(void*),由于在访问时只访问value,因此并不会指针造成越界。

ngx_hash_wildcard_init()解析:
nginx在处理通配符的时候运用了一些处理,即在利用了末尾两个bit位进行了数据的标识。

/**
 *利用末尾两个00位,由于内存对齐的缘故,末尾两个bit位必定为00
 *01表示value指向的是确定的数据,10和11指向下个哈希表
 */
name->value = (void *) ((uintptr_t) wdc | (dot ? 3 : 2));

name->value = (void *) ((uintptr_t) name->value | 1);

最后用ngx_hash_init将curr_names.elt装配成通用的哈希表。

ngx_uint_t ngx_hash_key_lc(): 一般由ngx_hash_init_t里面的函数指针ngx_hash_key_pt指向该函数。

ngx_int_t ngx_hash_add_key()解析:

if (flags & NGX_HASH_WILDCARD_KEY){
...
//如果有多个*或者两个.连续,直接返回错误
if (key->data[i] == '*'){...}	
if (key->data[i] == '.' && key->data[i + 1] == '.'){...}
//.example.com的情况
if (key->len > 1 && key->data[0] == '.'){...}
//*.example.com的情况
if (key->data[0] == '*' && key->data[1] == '.'){...}
//www.example.*的情况
if (key->data[i - 2] == '.' && key->data[i - 1] == '*'){...}
...
}

wildcard是处理通配符放入相应的动态数组里面
例如:
原始字段: .example.com\0
dns_wc_head: com.example\0
dns_wc_head_hash: example.com\0
原始字段: *.example.com\0
dns_wc_head: com.example.\0
dns_wc_head_hash: example.com\0
原始字段: www.example.*\0
dns_wc_tail: www.example\0
dns_wc_tail_hash: www.example.\0

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值