老规矩,先来看下ngx_hash大概的结构图:

注意: name的剩余字节是不在ngx_hash_elt_t的结构体占用内存中的,这里是个奇技淫巧,一定程度上保证key的不定长和内存浪费

在进入源码分析之前,先上一段debug用的示例代码

#include <stdio.h>
#include <string.h>
#include "nginx.h"
#include "ngx_hash.h"
#include "ngx_string.h"
#include "ngx_config.h"
#include "ngx_conf_file.h"
#include "ngx_core.h"
#include "ngx_palloc.h"
#define POOL_SIZE 2000typedef struct Test_2
{int a ;int b ;ngx_queue_t testq;
} test2;void testHash(){//demo中没有其他位置在启动时设置缓存行大小,此处调用cpuinfo()设置缓存行//在后面元素和bucket内存对齐时需要用到缓存行ngx_cpuinfo();u_short *test = test = ngx_alloc(4 * sizeof(u_short), NULL);;test[0] = 58;test[1] = 23;test[2] = 121;test[3] = 140;int a = 0;for( a = 0; a<4;a++){printf("test: %d \n" , test[a]);test[a] = (u_short) (ngx_align(test[a], ngx_cacheline_size));printf("test: %d \n" , test[a]);}ngx_pool_t * pool = ngx_create_pool(POOL_SIZE,NULL);ngx_array_t *arr = ngx_array_create(pool, 32, sizeof(ngx_hash_key_t));ngx_hash_keys_arrays_t haArr;ngx_memzero(&haArr, sizeof(ngx_hash_keys_arrays_t));haArr.temp_pool = pool;ngx_hash_keys_array_init(&haArr,NGX_HASH_LARGE_ASIZE);printf("size of ngx_hash_elt_t: %d \n" , sizeof(ngx_hash_elt_t) );printf("size of ngx_hash_key_t: %d \n" , sizeof(ngx_hash_key_t) );printf("size of u_short: %d \n" , sizeof(u_short) );printf("size of void*: %d \n" , sizeof(void*) );printf("size of char: %d \n" , sizeof(u_char) );test2 value[5];ngx_str_t key0 = ngx_string("bbbbb-key");ngx_str_t key1 = ngx_string("aaaaa-key");ngx_str_t key2 = ngx_string("ccccc-key");ngx_str_t key3 = ngx_string("ggggg-key");ngx_str_t key4 = ngx_string("kkkkk-key");ngx_uint_t       i;for(i = 0;i<5;i++){value[i].a = i;value[i].b = i*i;}ngx_hash_init_t *hash;hash = ngx_palloc(pool, sizeof(ngx_hash_init_t));hash->pool = pool;hash->name = "test_2_hash";hash->key = ngx_hash_key;hash->max_size = 128;hash->bucket_size = 64/*sizeof(test2) *8*/;ngx_hash_add_key(&haArr,&key0,&value[0],NGX_HASH_READONLY_KEY);ngx_hash_add_key(&haArr,&key1,&value[1],NGX_HASH_READONLY_KEY);ngx_hash_add_key(&haArr,&key2,&value[2],NGX_HASH_READONLY_KEY);ngx_hash_add_key(&haArr,&key3,&value[3],NGX_HASH_READONLY_KEY);ngx_hash_add_key(&haArr,&key4,&value[4],NGX_HASH_READONLY_KEY);int testsize = hash->bucket_size / (2 * sizeof(void *));ngx_hash_key_t *names = haArr.keys.elts;test2 *value00 = names[0].value;test2 *value01 = names[1].value;ngx_hash_init(hash,haArr.keys.elts,haArr.keys.nelts);test2 *node = ngx_hash_find(hash->hash, names[2].key_hash, key2.data, key2.len);
}
void testHashWC(){ngx_cpuinfo();u_char a[10] = "hElLo";u_char b[10] = "";u_char *c = a;u_char *d = b;int n = ngx_strlen(a);while (n--) {u_char e = *c;*d = ngx_tolower(e);c++;d++;}ngx_strlow(b,a,3);
//    每个地址对应8bit的内存空间(最小内存单元,1字节)
//    在64位系统中,内存对齐8字节(跨8个地址),对齐后的相邻指针相差8,因此指针二进制的后三位为0(32位系统,后两位为0);
//    因此获取的指针地址都是8的倍数void *ptra = (void *) ((uintptr_t) a );void *ptr = (void *) ((uintptr_t) a | 1);void *ptr1 = (void *) ((uintptr_t) a | 2);void *ptr2 = (void *) ((uintptr_t) a | 3);ngx_pool_t * pool = ngx_create_pool(POOL_SIZE,NULL);ngx_hash_keys_arrays_t haArr;ngx_memzero(&haArr, sizeof(ngx_hash_keys_arrays_t));haArr.temp_pool = pool;ngx_hash_keys_array_init(&haArr,NGX_HASH_LARGE_ASIZE);test2 value[6];//    ngx_str_t *key0 = ngx_pcalloc(pool, sizeof(ngx_str_t));
//    key0->data = "*.com.cn"; //ngx_strlow不能改变常量,因此data的值不能是常量
//    key0->len = strlen("*.com.cn");ngx_str_t key0 = ngx_string("*.com.cn");key0.data = ngx_pcalloc(pool, key0.len);ngx_memcpy(key0.data,"*.com.cn",key0.len);ngx_str_t key1 = ngx_string("*.a.com.cn");key1.data = ngx_pcalloc(pool, key1.len);ngx_memcpy(key1.data,"*.a.com.cn",key1.len);ngx_str_t key2 = ngx_string(".b.com.cn");key2.data = ngx_pcalloc(pool, key2.len);ngx_memcpy(key2.data,".b.com.cn",key2.len);ngx_str_t key3 = ngx_string("cn.org.*");key3.data = ngx_pcalloc(pool, key3.len);ngx_memcpy(key3.data,"cn.org.*",key3.len);ngx_str_t key4 = ngx_string("*.test.cc");key4.data = ngx_pcalloc(pool, key4.len);ngx_memcpy(key4.data,"*.test.cc",key4.len);ngx_str_t key5 = ngx_string(".com.cn");key5.data = ngx_pcalloc(pool, key5.len);ngx_memcpy(key5.data,".com.cn",key5.len);ngx_str_t key6 = ngx_string("*.c.com.cn");key6.data = ngx_pcalloc(pool, key6.len);ngx_memcpy(key6.data,"*.c.com.cn",key6.len);ngx_uint_t       i;for(i = 0;i<6;i++){value[i].a = i;value[i].b = i*i;}ngx_hash_init_t *hash;hash = ngx_palloc(pool, sizeof(ngx_hash_init_t));hash->pool = pool;hash->temp_pool = pool;hash->name = "test_2_hash";hash->key = ngx_hash_key;hash->max_size = 128;hash->bucket_size = 64/*sizeof(test2) *8*/;ngx_hash_add_key(&haArr,&key0,&value[0],NGX_HASH_WILDCARD_KEY);//*.com.cn与.com.cn在dns_wc_head_hash中都是com.cn。因此这里.com.cn不能添加ngx_hash_add_key(&haArr,&key5,&value[5],NGX_HASH_WILDCARD_KEY);ngx_hash_add_key(&haArr,&key1,&value[1],NGX_HASH_WILDCARD_KEY);ngx_hash_add_key(&haArr,&key2,&value[2],NGX_HASH_WILDCARD_KEY);ngx_hash_add_key(&haArr,&key3,&value[3],NGX_HASH_WILDCARD_KEY);ngx_hash_add_key(&haArr,&key4,&value[4],NGX_HASH_WILDCARD_KEY);ngx_hash_add_key(&haArr,&key6,&value[6],NGX_HASH_WILDCARD_KEY);ngx_hash_key_t *names = haArr.dns_wc_head.elts;test2 *value00 = names[0].value;test2 *value01 = names[1].value;ngx_hash_wildcard_init(hash,haArr.dns_wc_head.elts,haArr.dns_wc_head.nelts);ngx_str_t test_0 = ngx_string("e.com.cn");test2 *node = ngx_hash_find_wc_head(hash->hash,test_0.data,test_0.len);ngx_str_t test_1 = ngx_string("a.a.com.cn");test2 *node1 = ngx_hash_find_wc_head(hash->hash,test_1.data,test_1.len);ngx_str_t test_2 = ngx_string(".com.cn");test2 *node2 = ngx_hash_find_wc_head(hash->hash,test_2.data,test_2.len);
//    ngx_hash_wildcard_init(hash,haArr.dns_wc_tail.elts,haArr.dns_wc_tail.nelts);
}
void main(){//    testHash();testHashWC();
}

1、key数组初始化:ngx_hash_keys_array_init
key数组的初始化,主要是给ngx_hash_keys_arrays_t中的keys,dns_wc_head,dns_wc_tail,几个数组申请内存空间,ngx_hash_keys_arrays_t的主要成员:

    ngx_uint_t        hsize;            //hash表bucket(槽)个数ngx_array_t       keys;             //普通(非通配符)key数组ngx_array_t      *keys_hash;        //数组的指针,可以指向多个数组,将keys,按hash % hsize分组存放。//实际上是一个二维数组。ngx_array_t       dns_wc_head;     //前缀通配符的key数组,如:*.com.cnngx_array_t      *dns_wc_head_hash;ngx_array_t       dns_wc_tail;     //后缀通配符的key数组,如:test.com*ngx_array_t      *dns_wc_tail_hash;


2、添加key:ngx_hash_add_key
添加key有两种类型的,一个是NGX_HASH_READONLY_KEY普通的key,一个是
NGX_HASH_WILDCARD_KEY通配符的key。

ngx_hash_add_key(&haArr,&key0,&value[0],NGX_HASH_READONLY_KEY);
//or
ngx_hash_add_key(&haArr,&key0,&value[0],NGX_HASH_WILDCARD_KEY);


通配符key的处理:

demo结果如图:

3、普通hash表初始化:ngx_hash_init

方法的最开始,对几个参数做了限制:
max_size不能为0;单个bucket大小不能超过65536-缓存行大小,同时由于bucket之间有个void指针相隔,因此单个bucket的大小不能小于一个ngx_hash_elt_t+一个void指针的大小。由于做了指针对齐,因此ngx_hash_elt_t的大小最小都要两个void指针的大小。bucket的大小最小都要三个void指针大小;64位系统中,一个指针大小8个字节,因此bucket最小3*8 = 24;

对齐方法解析
#define ngx_align(d, a) (((d) + (a - 1)) & ~(a - 1))//a为2的n次幂
假设a=8,那么a-1二进制为00000111,d的低四位任一一位只要为1,d+(a-1)后,d的第四位或者高于第四位的某一位肯定进1。也就是d的4位之前的值肯定是8的倍数。如d = 17;二进制为00010001,
c = 00010011 + 00000111 == 00011010,第四位之前的00011000值为24,为8的倍数,那么怎么把c的后三位清零了:c & ~(a-1)即可: 00011010 & 11111000 = = 00011000

几个结构体区分
ngx_hash_t,ngx_hash_wildcard_t这两个是hash表,表结构体
ngx_hash_key_t这个是建立hash表前的每个key的结构体
ngx_hash_elt_t 这个是建立hash表后,每个bucket中的元素的结构体。

4、普通hash表查询元素:ngx_hash_find

5、通配符hash初始化:ngx_hash_wildcard_init
方法开始先初始化了两个数组curr_names与next_names,curr_names一个记录当前同级的每个name; next_names则记录当前name第一个点的后续字符串。
下面是通配符hash生成过程:

效果如下:

注意这里:cn.com.c中间有个cc.test.从源码看没有在cn.com时一起处理。而是以完成的cn.com.c另起炉灶创建通配符hash表。计算hash时,都是以cn开头计算hash,则不管最开始的names需要多少个bucket,cn.com.c的通配符hash与cn.com都是放在同一个bucket里面。(即hash冲突场景)

关于对齐指针的后两位为00:
每个地址对应8bit的内存空间(最小内存单元,1字节)
在64位系统中,内存对齐8字节(跨8个地址),对齐后的相邻指针相差8,因此指针二进制的后三位为0(32位系统,后两位为0);
因此获取的指针地址都是8的倍数

6、通配符hash表元素查找:ngx_hash_find_wc_head(ngx_hash_find_wc_tail)
这里仅以ngx_hash_find_wc_head为例:
从上面的图可以知道,通配符字符串的每一点分级后,每个分级的结构为:
hwc(或hhint)= =>ngx_hash_t = =>*bucket = =>elt.value= =>下一级hwc(或匹配的数据)

  • 当hash表以 *.com.cn构建:即从name = cn.com.开始:一并构建hash的name还有cn.com.a. 与cn.com.b
    查找输入为 com.cn : 无法匹配到hash节点;
    e.com.cn : 查找到com所在hwc节点的value;即最长匹配到.com.cn
    .com.cn : 同e.com.cn
    cn : 直接查找第一级的普通hash,但value肯定是null;
    a.a.com.cn: 最长匹配到a,匹配到a数据结点的value;
  • 当hash表以 .com.cn构建:
    查找输入为com.cn :查找到com所在hwc节点的value;即最长匹配到.com.cn
    其他的输入与*.com.cn构建的hash表一样。

总的来说,通配符的hash,ngx做得挺啰嗦的,个人感觉以点开头或结尾的通配符配置方式没有必要存在,只需要处理*号开头或者结尾的通配字符串,毕竟约定大于配置,搞那么多兼容的配置代码复杂,配置也复杂。不过也可能跟毛子的习惯有关。

参考:
菜鸟nginx源码剖析
Nginx源码分析

NGINX源码之:ngx_hash相关推荐

  1. Nginx 源码分析:ngx_hash_t(上)

    源文件路径 版本:1.8.0 csrc\core\Ngx_hash.h src\core\Ngx_hash.c 关于hash表 Nginx实现的hash表和常见的hash表大体一致,细节有区别,所以, ...

  2. NGINX源码之:ngx_bufchain

    老规矩,先来看下buf和chain的大概结构 首先温习下指针的指针,在ngx_buf的几个方法中用到了chain指针的指针,用于chain链表的遍历值替换. test2 * test;test2 ** ...

  3. Nginx源码分析之ngx_hash_t

    源码位置: nginx/src/core/ngx_hash.h nginx/src/core/ngx_hash.c (一)数组与hash表 从查询的角度来看,数组根据索引值的查询速度很快快. 原因在于 ...

  4. Nginx 源码分析

    1.工程 ngx_conf_file.c ngx_connection.c ngx_cycle.c ngx_file.h ngx_module.c ngx_open_file_cache.h ngx_ ...

  5. NGINX源码之:ngx_arrayngx_listngx_string

    1.array 老规矩,先看下array的结构 /* 数组Array结构体 */ typedef struct {void *elts; /* 指向数组第一个元素*/ngx_uint_t nelts; ...

  6. NGINX源码之:目录导航

    Nginx版本1.20 debug用的源码码云地址点这里 本人debug用的cento7的图形界面系统,cento7中安装clion2020.1 NGINX源码依赖安装yum -y install g ...

  7. NGINX源码之:ngx_queue

    老规矩,先看下ngx_queue的大概结构图 debug所用的示例demo: #include <stdio.h> #include <string.h> #include & ...

  8. 从Nginx源码谈大小写字符转化的最高效代码以及ASCII码表的科学

    说起大小写字母转换,大家很容易想起系统函数是不是,几乎所有的编程语言都提供了这种转换函数,但是你有没有想过这背后是怎么实现的? 让你写怎么实现? 我们都知道Nginx是目前用的最多的Http服务器,那 ...

  9. Nginx源码分析链接

    nginx-0.8.38源码探秘:http://blog.csdn.net/ccdd14/article/details/5872312 nginx源码分析: http://blog.sina.com ...

最新文章

  1. 如何教计算机认识手写数字(上)
  2. Science:便携式DNA测序仪在检测病毒疫情中大显身手
  3. UML实践详细经典教程
  4. 《C++ 开发从入门到精通》——1.3 使用Visual Studio 2010
  5. 【Linux高级驱动】如何分析并移植网卡驱动
  6. NET内存持续增长问题排查
  7. 新近碰到的病毒(TR.Spy.Babonock.A)
  8. Android样式开发---shape
  9. 跨平台加密版 SQLite 3 - wxSQLite3
  10. ilitek win10 触摸屏驱动_想做多大尺寸触摸框找融创方圆定制触摸屏工厂
  11. android 捕获Home键和ACTION_TIME_TICK广播
  12. html52D转换3D,CSS3 Transform 2D和3D转换
  13. 德佑地产房产经纪人区域总监访谈:有肌肉,更有内涵!
  14. Google Maps API 中的标注编程
  15. 抖音为何能一夜爆火?
  16. js 数组去重、扁平化函数
  17. 【畅购商城】用户登录
  18. 【分享程序员的生活】在国企里面当程序猿是一种怎样的体验!!??
  19. OSI与TCP/IP各层的结构与功能
  20. 【密码算法 之七】GCM 浅析

热门文章

  1. PMP之总价合同、成本补偿合同、工料合同
  2. linux crontab清理脚本,crontab定时清理日志
  3. WP模板兔模板V4.3 去除授权+多功能插件
  4. Codeforces 524C Idempotent functions
  5. 循环抓取xkcd首页图片
  6. 数学定理【转自百度百科】
  7. IE浏览器打不开网页
  8. python随机生成一个数字_如何实现python随机生成数字?
  9. 【TED ON FLEX】支持flashplayer的RIBBIT系统
  10. 转-STAF学习使用总结一