ngx_hash散列表

本文详细介绍了 Nginx 中散列表的数据结构及其初始化过程。涵盖了散列表槽的结构、散列表结构、散列表初始化所用的结构体等,并解释了如何通过 ngx_hash_init 函数将元素散列到散列表中。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ngx_hash_t散列表(全匹配)

散列表槽的结构

typedef struct {
    /* 指向用户定义元素数据的指针,如果当前ngx_hash_elt_t槽为空,则value=NULL */
    void            *value;
    /* 元素关键字长度 */
    u_short     len;
    /* 元素关键字首地址 */
    u_char          name[1];
} ngx_has_elt_t;

散列表结构

typedef struct {
    /* 指向散列表的首地址,即第一个槽的地址 */
    ngx_hash_elt_t  **buckets;
    /* 散列表中槽的个数 */
    ngx_uint_t      size;
};

散列表初始化所用的结构体

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      *temp_pool;
} ngx_hash_init_t;

/* ngx_hash_init初始化函数参数中要用的结构体 *
typedef struct {
    //元素关键字
    ngx_str_t           key;
    //由散列方法计算出来的关键字
    ngx_uint_t      key_hash;
    //指向实际的用户数据
    void                *value;
} ngx_hash_key_t;

/* 下面这个结构体又是用来获得上述ngx_hash_key_t的 */
typedef struct {
    /* 下面的keys_hash、dns_wc_head_hash、dns_wc_tail_hash都是简易散列表,而hsize指明了散列表的槽的个数,
    其简易散列方法也需要对hsize求余 */
    ngx_uint_t      hsize;
    /* 内存池,分配永久性内存 */
    ngx_pool_t      *pool;
    /* 临时内存池 */
    ngx_pool_t      *temp_pool;

    /* 用动态数组保存不包含通配符关键字元素的ngx_hash_key_t */
    ngx_array_t     keys;
    /* 一个极其简易的散列表,它以数组的形式保存hszie个ngx_array_t元素。在用户添加元素的过程中,会根据关键码
    将用户的ngx_str_t类型的关键字添加到ngx_array_t中,这里用户元素的关键字都不带通配符。 */
    ngx_array_t     *keys_hash;

    /* 用动态数组保存包含前置通配符关键字元素的ngx_hash_key_t */
    ngx_array_t     dns_wc_head;
    /* 一个极其简易的散列表,它以数组的形式保存hszie个ngx_array_t元素。在用户添加元素的过程中,会根据关键码
    将用户的ngx_str_t类型的关键字添加到ngx_array_t中,这里用户元素的关键字都带前置通配符。 */
    ngx_array_t     *dns_wc_head_hash;

    /* 用动态数组保存包含后置通配符关键字元素的ngx_hash_key_t */
    ngx_array_t     dns_wc_tail;
    /* 一个极其简易的散列表,它以数组的形式保存hszie个ngx_array_t元素。在用户添加元素的过程中,会根据关键码
    将用户的ngx_str_t类型的关键字添加到ngx_array_t中,这里用户元素的关键字都带后置通配符。 */
    ngx_array_t     *dns_wc_tail_hash;
} ngx_hash_keys_arrays_t;

散列表的结构示意图

散列表的结构示意图

散列表常用函数

函数名参数说明执行意义
ngx_int_t ngx_hash_init(ngx_hash_init_t *hinit,ngx_hash_key_t *names,ngx_uint_t nelts)hinit是散列表初始化结构体的指针;names是数组的首地址,这是一个ngx_hash_key_t类型的数组,将要被添加如散列表;nelts是数组元素的个数将names数组散列到散列表中
typedef ngx_uint_t (*ngx_hash_key_pt)(u_char *data,size_t len);data是元素关键字的首地址;len是元素关键字长度用来自定义散列方法
void *ngx_hash_find(ngx_hash_t *hash,ngx_uint_t key,u_char *name,size_t len)hash是散列表结构体的指针;key是待查找元素的散列而得的关键字;name和len表示实际关键字的地址和长度查询ngx_hash_elt_t中value所指向的用户数据

利用ngx_hash_keys_arrays_t获得ngx_hash_key_t数组,再交由ngx_hash_init函数,就能生成一个散列表。



ngx_hash_keys_arrays_t提供函数

函数名参数说明执行意义
ngx_int_t ngx_hash_keys_array_init(ngx_hash_keys_arrays_t *ha,ngx_uint_t type)ha是要初始化的ngx_hash_keys_arrays_t指针;type:NGX_HASH_SMALL,初始化元素较少;NGX_HASH_LARGE,初始化元素较多初始化结构体ngx_hash_keys_arrays_t,在想ha添加元素前必须调用
ngx_int_t ngx_hash_add_key(ngx_hash_keys_arrays_t *ha,ngx_str_t *key,void *value,ngx_uint_t flags)ha同上;key是添加元素的关键字;value是对应的用户数据指针;flags:NGX_HASH_WILDCARD_KEY表示处理通配符;NGX_HASH_READONLY_KEY表示关键字不可更改;其他值表示不处理通配符,又允许修改关键字来散列想ha中添加一个元素

这里要注意的是,不要搞混了用户数据、用户数据关键字和散列关键字

  • 用户数据:未知,用户自己定义的内存指针即可;
  • 用户数据关键字:这是用户数据中的某个成员,散列表根据它来进行散列;
  • 散列关键字:根据用户数据关键字散列而得的数字。
Nginx 中的 `ngx_hash_t` 是一个高效且轻量级的数据结构,用于实现哈希表。它主要用于处理字符串键值对,适用于 Nginx 内部的各种配置和运行时数据管理场景[^3]。 ### 基本用途 `ngx_hash_t` 通常用于存储静态或动态生成的键值对,例如在处理 HTTP 请求时,用于快速查找配置信息、变量映射等。它的设计目标是提供高效的查找性能,并支持并发访问。 ### 实现细节 #### 结构定义 `ngx_hash_t` 的核心结构如下: ```c typedef struct { ngx_hash_elt_t **buckets; // 指向桶数组的指针 ngx_uint_t size; // 哈希表的大小(桶的数量) } ngx_hash_t; ``` 每个桶是一个指向 `ngx_hash_elt_t` 类型的指针,表示哈希冲突链表的头节点。 #### 元素结构 `ngx_hash_elt_t` 是哈希表中存储元素的基本单元,其定义如下: ```c typedef struct { uintptr_t value; // 存储对应的值 u_short len; // 键的长度 u_char name[1]; // 键的实际内容(柔性数组) } ngx_hash_elt_t; ``` 该结构允许存储任意长度的字符串键及其对应的值。键的长度通过 `len` 字段指定,而 `name[1]` 则用于存储实际的字符串内容。 #### 初始化与销毁 `ngx_hash_t` 的初始化通常通过 `ngx_hash_init` 函数完成,该函数会根据提供的键值对集合分配足够的内存并构建哈希表。销毁操作则通过 `ngx_hash_destroy` 完成,释放所有相关的内存资源。 #### 插入与查找 插入操作通过 `ngx_hash_add_key` 函数完成,该函数会计算键的哈希值,并将其插入到相应的桶中。如果发生哈希冲突,则使用链表的方式将新元素添加到现有元素之后。 查找操作通过 `ngx_hash_find` 函数完成,该函数根据给定的键计算哈希值,并遍历对应的桶中的链表以找到匹配的元素。 #### 性能优化 Nginx 的 `ngx_hash_t` 实现采用了多种优化策略: - **预分配内存**:为了减少内存碎片和提高性能,`ngx_hash_t` 使用了内存池 (`ngx_pool_t`) 来管理内存分配。 - **负载因子控制**:通过合理设置哈希表的大小,确保负载因子保持在一个较低的水平,从而减少哈希冲突的发生。 - **线程安全**:虽然 Nginx 主要采用单线程异步事件驱动模型,但在多线程环境中,`ngx_hash_t` 的实现也考虑到了线程安全性,避免并发访问导致的数据竞争问题。 ### 示例代码 以下是一个简单的示例,展示如何使用 `ngx_hash_t` 进行哈希表的初始化、插入和查找操作: ```c #include <ngx_config.h> #include <ngx_core.h> #include <ngx_http.h> static ngx_int_t init_hash(ngx_conf_t *cf, ngx_hash_t *hash) { ngx_str_t keys[] = { ngx_string("key1"), ngx_string("key2"), ngx_null_string }; ngx_uint_t i; ngx_array_t *array; array = ngx_array_create(cf->pool, 4, sizeof(ngx_hash_key_t)); if (array == NULL) { return NGX_ERROR; } for (i = 0; !ngx_is_null(&keys[i]); i++) { ngx_hash_key_t *key = ngx_array_push(array); if (key == NULL) { return NGX_ERROR; } key->key = keys[i]; key->key_hash = ngx_hash_key_lc(keys[i].data, keys[i].len); key->value = (void *)123456; } if (ngx_hash_init(hash, array->elts, array->nelts, cf->size, cf->name) != NGX_OK) { return NGX_ERROR; } return NGX_OK; } static void find_in_hash(ngx_hash_t *hash, ngx_str_t *key) { ngx_uint_t hash_value = ngx_hash_key_lc(key->data, key->len); ngx_hash_elt_t *elt = ngx_hash_find(hash, hash_value, key->data, key->len); if (elt != NULL) { ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0, "Found value: %p", elt->value); } else { ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0, "Key not found"); } } ``` 在这个示例中,首先通过 `init_hash` 函数初始化了一个哈希表,并插入了一些键值对。然后通过 `find_in_hash` 函数进行查找操作,输出对应的值。 ### 总结 `ngx_hash_t` 是 Nginx 中一个非常重要的数据结构,广泛应用于各种需要高效查找的场景。它的设计兼顾了性能与内存使用的平衡,能够很好地适应 Nginx 的高并发、低延迟需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值