nginx源碼分析之hash的實現


  nginx實現了自己的hash數據結構,正如數據結構中講述的那樣,nginx用開放鏈表法解決沖突,不過不同的是一旦一個hash表被初始化后就不會被修改,即插入和刪除,只進行查詢操作,所以nginx通過計算初始化時key的個數來確定hash表中桶的個數和每個桶的容量,這樣能最大限度的利用內存資源。雖然用開放鏈表法,實際上每個桶都是一塊連續的內存空間。nginx實現了兩類hash結構,一類是key中包含通配符的ngx_hash_wildcard_t,另一類則是key中不包含通配符的ngx_hash_t,這里的通配符指*號。之所以會有這兩類hash,是因為nginx主要用hash來存放url與ip的對應關系,目的是為了通過url來查找ip,而url是可以用通配符表示的,如*.baidu.com。接下來先通過深入到nginx源碼中看看hash中定義的數據結構,然后看看兩類hash是怎樣實現的,最后通過一個例子來使用這兩類hash。

1 相關的數據結構

1.1 ngx_hash_t結構

  這類hash的數據結構定義如下:

 1 typedef struct {
 2     void             *value;  //ip,也就是<key,value>中的key  3     u_short           len;  //url長度  4     u_char            name[1];  //url,也就是<key,value>中的value  5 } ngx_hash_elt_t;
 6 
 7 
 8 typedef struct {
 9     ngx_hash_elt_t  **buckets;  //每個桶的起始地址 10     ngx_uint_t        size;  //桶的個數 11 } ngx_hash_t;

  ngx_hash_t結構管理這個hash,ngx_hash_elt_t是hash桶中一個元素的表示。ngx_hash_elt_t字段中的value既可以指向實際的ip,也可以指向另一個hash表(這種情況只能出現在包含通配符的hash中),name字段既可以指向完整的url,也可以指向url的一部分(這種情況也只能出現在包含通配符的hash中)。

1.2 ngx_hash_wildcard_t

  這個結構主要用於包含通配符的hash的,具體如下:

1 typedef struct {
2     ngx_hash_t        hash;
3     void             *value;
4 } ngx_hash_wildcard_t;

  這個結構相比ngx_hash_t結構就是多了一個value指針,value這個字段是用來存放某個已經達到末尾的通配符url對應的value值,如果通配符url沒有達到末尾,這個字段為NULL。

1.3 ngx_hash_init_t

  ngx_hash_init_t結構主要用於提供創建一個hash所需要的一些信息,定義如下:

 1 typedef struct {
 2     ngx_hash_t       *hash;  //指向待新建的hash表  3     ngx_hash_key_pt   key;  //hash函數指針  4 
 5     ngx_uint_t        max_size;  //hash表中桶的最大值,實際桶的個數存放在前面ngx_hash_t中的size字段中  6     ngx_uint_t        bucket_size;  //每個桶的最大尺寸  7 
 8     char             *name;  //hash表的名字,其實沒啥用(在log中用)  9     ngx_pool_t       *pool;  //構建hash所用的內存池 10     ngx_pool_t       *temp_pool;  //構建hash所用的臨時內存池 11 } ngx_hash_init_t;

  hash字段如果為NULL的話,會動態創建管理hash的結構ngx_hash_t,這種情況通常在包含通配符的情況下使用;如果不為NULL,則使用這個字段指向的ngx_hash_t結構,這種情況通常在不包含通配符的情況下使用。

1.4 ngx_hash_key_t

  ngx_hash_key_t結構主要用於初始化hash表的,正如前面說的,nginx只能一次性的初始化,初始化之后就不能插入和修改了,所以用一個ngx_table_elt_t結構的數組來存放要插入到hash中的所有<key,value>對,所有初始化hash的函數都需要根據這樣一個數組來初始化hash,這個在后面初始化hash函數的時候可以看到。

1 typedef struct {
2     ngx_str_t         key;  //<key,value>中的key 3     ngx_uint_t        key_hash;  //key通過hash函數算出的hash值 4     void             *value;  //<key,value>中的value 5 } ngx_hash_key_t;

1.5 ngx_hash_combined_t

1 typedef struct {
2     ngx_hash_t            hash;
3     ngx_hash_wildcard_t  *wc_head;
4     ngx_hash_wildcard_t  *wc_tail;
5 } ngx_hash_combined_t;

  這個結構包含了三類hash,hash字段表示不保護通配符的hash,wc_head字段表示包含前綴通配符的hash,wc_tail表示包含后綴通配符的hash。由於用戶配置的url通常會是是不包含通配符的url,包含前綴通配符的url和包含后綴通配符的rul中的一種或多種,所以一般使用這個結構來完全表示用戶配置的url。

1.6 ngx_hash_keys_arrays_t

 1 typedef struct {
 2     ngx_uint_t        hsize;
 3     ngx_pool_t       *pool;
 4     ngx_pool_t       *temp_pool;  
 5 
 6     ngx_array_t       keys;  //存放不包含通配符的<key,value>鍵值對  7     ngx_array_t      *keys_hash;  //用來檢測沖突的
 8 
 9     ngx_array_t       dns_wc_head;  //存放包含前綴通配符的<key,value>鍵值對 10     ngx_array_t      *dns_wc_head_hash;  //用來檢測沖突的
11 
12     ngx_array_t       dns_wc_tail;  //存放包含后綴通配符的<key,value>鍵值對 13     ngx_array_t      *dns_wc_tail_hash; //用來檢測沖突的
14 } ngx_hash_keys_arrays_t;

  這個結構存放了初始化hash需要的所有鍵值對,keys數組用來初始化不包含通配符的hash,dns_wc_head數組用來初始化包含前綴通配符的hash,dns_wc_tail數組用來初始化包含后綴通配符的hash,並且dns_wc_head和dns_wc_tail數組包含的<key,value>鍵值對中的key都是已經去掉通配符的key,具體情況在后面ngx_hash_add_key函數的介紹中會詳細說明。

2 相關函數

2.1 ngx_hash_init

  這個函數用來初始化不包含通配符的hash,函數原型如下:

1 ngx_int_t
2 ngx_hash_init(ngx_hash_init_t *hinit, ngx_hash_key_t *names, ngx_uint_t nelts)

  hinit參數是初始化hash的相關信息,names存放了所有需要插入到hash中的<key,value>對,nelts是<key,value>對的個數。這個函數主要做了4部分的工作:

  (1)檢查bucket_size是否合法,也就是它的值必須保證一個桶至少能存放一個<key,value>鍵值對,具體如下:

 1 #define NGX_HASH_ELT_SIZE(name)                                               \
 2     (sizeof(void *) + ngx_align((name)->key.len + 2, sizeof(void *)))
 3 
 4 for (n = 0; n < nelts; n++) {
 5         if (hinit->bucket_size < NGX_HASH_ELT_SIZE(&names[n]) + sizeof(void *))
 6         {
 7             ngx_log_error(NGX_LOG_EMERG, hinit->pool->log, 0,
 8                           "could not build the %s, you should "
 9                           "increase %s_bucket_size: %i",
10                           hinit->name, hinit->name, hinit->bucket_size);
11             return NGX_ERROR;
12         }
13     }

  上面的for循環保證hash的桶至少能裝一個<key,value>鍵值對,宏NGX_HASH_ELT_SIZE用來計算一個ngx_hash_key_t表示一個實際的<key,value>鍵值對占用內存的大小,之所以NGX_HASH_ELT_SIZE(&names[n]) 后面需要加上sizeof(void *),主要是每個桶都用一個值位NULL的void*指針來標記結束。

  (2)計算hash中桶的個數

 1     bucket_size = hinit->bucket_size - sizeof(void *);  //除去桶標記后桶的大小  2     start = nelts / (bucket_size / (2 * sizeof(void *)));  //桶的最小個數  3     start = start ? start : 1;
 4     if (hinit->max_size > 10000 && nelts && hinit->max_size / nelts < 100) {
 5         start = hinit->max_size - 1000;
 6     }
 7     //更新size來滿足bucket_size
 8     for (size = start; size < hinit->max_size; size++) {
 9         ngx_memzero(test, size * sizeof(u_short));
10         for (n = 0; n < nelts; n++) {
11             if (names[n].key.data == NULL) {
12                 continue;
13             }
14             key = names[n].key_hash % size;  //計算當前<key,value>在哪個桶 15             test[key] = (u_short) (test[key] + NGX_HASH_ELT_SIZE(&names[n]));  //計算當前<key,value>插入到桶之后桶的大小 16 
17             if (test[key] > (u_short) bucket_size) {  //檢查桶是否溢出了 18                 goto next;
19             }
20         }
21         goto found;
22     next:
23         continue;
24     

  從最小桶的個數開始遞增,直到所有的<key,value>鍵值對都能存放在對應的桶中不溢出,那當前的桶個數就是需要的桶個數。

   (3)計算新創建的hash所占用的空間,並調用內存分配函數分配這些空間

 1     for (i = 0; i < size; i++) {
 2         test[i] = sizeof(void *);
 3     }
 4   //計算每個桶的實際大小
 5     for (n = 0; n < nelts; n++) {
 6         if (names[n].key.data == NULL) {
 7             continue;
 8         }
 9 
10         key = names[n].key_hash % size;
11         test[key] = (u_short) (test[key] + NGX_HASH_ELT_SIZE(&names[n]));
12     }
13   //計算所有桶的大小
14     len = 0;
15 
16     for (i = 0; i < size; i++) {
17         if (test[i] == sizeof(void *)) {
18             continue;
19         }
20 
21         test[i] = (u_short) (ngx_align(test[i], ngx_cacheline_size));  //每個桶大小滿足cache行對齊 22 
23         len += test[i];
24     }

  上面根據實際的<key,value>鍵值對來實際計算每個桶的大小,而不是所有桶的大小的設置成一樣的,這樣能很有效的節約內存空間,當然由於每個桶的大小是不固定的,所有每個桶的末尾需要一個額外空間(大小為sizeof(void*))來標記桶的結束。並且每個桶大小滿足cache行對齊,這樣能加快訪問速度,從這里也可以看出nginx無處不在優化程序的性能和資源的使用效率。

 1     if (hinit->hash == NULL) {  //hash為NULL,則動態生成管理hash的結構  2         //calloc會把獲取的內存初始化為0
 3         hinit->hash = ngx_pcalloc(hinit->pool, sizeof(ngx_hash_wildcard_t)
 4                                              + size * sizeof(ngx_hash_elt_t *));
 5         if (hinit->hash == NULL) {
 6             ngx_free(test);
 7             return NGX_ERROR;
 8         }
 9 
10         buckets = (ngx_hash_elt_t **)
11                       ((u_char *) hinit->hash + sizeof(ngx_hash_wildcard_t));
12 
13     } else {
14         buckets = ngx_pcalloc(hinit->pool, size * sizeof(ngx_hash_elt_t *));
15         if (buckets == NULL) {
16             ngx_free(test);
17             return NGX_ERROR;
18         }
19     }
20 
21     elts = ngx_palloc(hinit->pool, len + ngx_cacheline_size);  //將所有桶占用的空間分配在連續的內存空間中 22     if (elts == NULL) {
23         ngx_free(test);
24         return NGX_ERROR;
25     }
26 
27     elts = ngx_align_ptr(elts, ngx_cacheline_size);
28 
29     for (i = 0; i < size; i++) {
30         if (test[i] == sizeof(void *)) {
31             continue;
32         }
33 
34         buckets[i] = (ngx_hash_elt_t *) elts;  //初始化每個桶的起始位置 35         elts += test[i];
36 
37     }

  上面代碼先動態分配每個桶的起始指針,然后動態分配所有桶的空間,然后根據每個桶的大小,將每個桶的起始指針初始化指向對應桶的起始地址。

  (4)將每個<key,value>鍵值對復制到所在的桶中

 1     for (i = 0; i < size; i++) {
 2         test[i] = 0;
 3     }
 4 
 5     for (n = 0; n < nelts; n++) {
 6         if (names[n].key.data == NULL) {
 7             continue;
 8         }
 9 
10         key = names[n].key_hash % size;  //計算當前<key,value>所在的桶 11         elt = (ngx_hash_elt_t *) ((u_char *) buckets[key] + test[key]);  //計算當前<key,value>所在桶中的位置 12         //將當前<key,value>的值復制到桶中
13         elt->value = names[n].value;
14         elt->len = (u_short) names[n].key.len;
15 
16         ngx_strlow(elt->name, names[n].key.data, names[n].key.len);
17         test[key] = (u_short) (test[key] + NGX_HASH_ELT_SIZE(&names[n]));  //更新當前桶的大小 18     }
19     //為了標記每個桶的結束
20     for (i = 0; i < size; i++) {
21         if (buckets[i] == NULL) {
22             continue;
23         }
24 
25         elt = (ngx_hash_elt_t *) ((u_char *) buckets[i] + test[i]);
26 
27         elt->value = NULL;  //前面每個test加上sizeof(void *)就是為了這個value指針
28     }

   通過調用ngx_hash_init函數,一個hash就建立起來了,該hash大概的情況如下圖所示:

2.2 ngx_hash_add_key

1 ngx_int_t
2 ngx_hash_add_key(ngx_hash_keys_arrays_t *ha, ngx_str_t *key, void *value,
3     ngx_uint_t flags)

  ha中包含了三類ngx_hash_key_t類型的數組,分別用來初始化三類hash(介紹ngx_hash_keys_arrays_t時已說明),那么新的<key,value>應該加入到哪個數組中就是ngx_hash_add_key這個函數的主要任務。參數中的flags用來標記是否key中可能包含通配符,一般這個參數設置為NGX_HASH_WILDCARD_KEY,即可能包含通配符。需要注意的是這個函數會改變包含通配符的key,將通配符去掉,如*.baidu.com會改變為com.baidu.,.baidu.com會改變為com.baidu,www.baidu.*會改變為www.baidu,www.baidu.這種通配是不允許出現的。

2.3 ngx_hash_find

1 void *
2 ngx_hash_find(ngx_hash_t *hash, ngx_uint_t key, u_char *name, size_t len)

  這個函數通過給定的key和name在hash表中查找對應的<name,value>鍵值對,並將查找到的value值返回,參數中的key是name通過hash計算出來的。這個函數的實現很簡單,就是通過key找到要查找的鍵值對在哪個桶中,然后遍歷這個桶中的每個元素找key等於name的元素。

2.4 ngx_hash_wildcard_init

1 ngx_int_t
2 ngx_hash_wildcard_init(ngx_hash_init_t *hinit, ngx_hash_key_t *names,
3     ngx_uint_t nelts)

  這個函數是nginx實現通配hash的關鍵所在,該函數通過對通配鍵值對建立多級hash來實現通配hash。實現的hash表大致如下圖所示:

 

  上面的圖只顯示了二級hash,實際上可以由多級hash。下面我們舉個實際的例子來加深理解,假設有下面這些鍵值對:

  <*.com, "220.181.111.147">,<*.baidu.com, "220.181.111.147">,<*.baidu.com.cn, "220.181.111.147">,<*.google.com,"58.63.236.35">

  (1)通過函數ngx_hash_add_key將上面的鍵值對加入到ngx_hash_keys_arrays_t結構中的dns_wc_head數組中,該數組的值如下圖所示:

  {key = ("com." , 4 ), key_hash = 0 , value = "220.181.111.147"}
  {key = ("cn.com.baidu." , 13), key_hash = 0 , value = "220.181.111.147"}
  {key = ("com.baidu." , 10), key_hash = 0 , value = "220.181.111.147"}
    {key = ("com.google." , 11), key_hash = 0 , value = "58.63.236.35"}

  (2)將上面的dns_wc_head數組傳遞給ngx_hash_wildcard_init,生成的hash如下圖所示:

   現在來看下ngx_hash_wildcard_init是怎樣實現上面圖所示的多級hash結構的。

 1     for (n = 0; n < nelts; n = i) {
 2 
 3         dot = 0;
 4         //以.作為字段的分隔符
 5         for (len = 0; len < names[n].key.len; len++) {
 6             if (names[n].key.data[len] == '.') {
 7                 dot = 1;
 8                 break;
 9             }
10         }
11      
12         name = ngx_array_push(&curr_names);
13         if (name == NULL) {
14             return NGX_ERROR;
15         }
16         //將上面獲取的字段作為當前hash的key
17         name->key.len = len;
18         name->key.data = names[n].key.data;
19         name->key_hash = hinit->key(name->key.data, name->key.len);
20         name->value = names[n].value;
21 
22         dot_len = len + 1;
23 
24         if (dot) {
25             len++;
26         }
27       //收集同一前綴的所有后綴
28         next_names.nelts = 0;
29 
30         if (names[n].key.len != len) {
31             next_name = ngx_array_push(&next_names);
32             if (next_name == NULL) {
33                 return NGX_ERROR;
34             }
35             
36             next_name->key.len = names[n].key.len - len;
37             next_name->key.data = names[n].key.data + len;
38             next_name->key_hash = 0;
39             next_name->value = names[n].value;
40         }
41 
42         for (i = n + 1; i < nelts; i++) {
43             if (ngx_strncmp(names[n].key.data, names[i].key.data, len) != 0) {
44                 break;
45             }
46 
47             if (!dot
48                 && names[i].key.len > len
49                 && names[i].key.data[len] != '.')
50             {
51                 break;
52             }
53 
54             next_name = ngx_array_push(&next_names);
55             if (next_name == NULL) {
56                 return NGX_ERROR;
57             }
58 
59             next_name->key.len = names[i].key.len - dot_len;
60             next_name->key.data = names[i].key.data + dot_len;
61             next_name->key_hash = 0;
62             next_name->value = names[i].value;
63         }
64 
65         if (next_names.nelts) {  //next_names中有元素
66 
67             h = *hinit;
68             h.hash = NULL;  
69             //遞歸建立當前字段的所有后綴字段組成的hash
70             if (ngx_hash_wildcard_init(&h, (ngx_hash_key_t *) next_names.elts,
71                                        next_names.nelts)
72                 != NGX_OK)
73             {
74                 return NGX_ERROR;
75             }
76 
77             wdc = (ngx_hash_wildcard_t *) h.hash;
78 
79             if (names[n].key.len == len) {  //當前字段已經達到末尾
80                 wdc->value = names[n].value;
81             }
82             //將后綴組成的下一級hash地址作為當前字段的value保存下來
83             name->value = (void *) ((uintptr_t) wdc | (dot ? 3 : 2));   //2只有在后綴通配符的情況下才會出現 84 
85         } else if (dot) {  //只有一個,而且不是后綴通配符
86             name->value = (void *) ((uintptr_t) name->value | 1);
87         }
88     }
89      //根據<當前字段,value>鍵值對建立hash
90     if (ngx_hash_init(hinit, (ngx_hash_key_t *) curr_names.elts,
91                       curr_names.nelts)
92         != NGX_OK)
93     {
94         return NGX_ERROR;
95     }
96 
97     return NGX_OK;
98 }

  怎樣標記一個鍵值對<key,value>中的value是指向實際的value,還是指向下一級的hash地址,這是上面代碼實現的一個巧妙的地方。由於每個hash表的地址或者實際value的地址都是以4字節對齊的,所以這些地址的低2位都是0,這樣通過這兩位的標記可以很好地解決這個問題。

2.5 ngx_hash_find_wc_head

1 void *
2 ngx_hash_find_wc_head(ngx_hash_wildcard_t *hwc, u_char *name, size_t len)

  這個函數根據name在前綴通配符hash中查找對應的value值。有了hash表后,查找是件相對更容易的事,從后往前的獲取name的每個字段(根據.分割),用每個字段查hash表,如果獲取的value值的標記存在下一級hash,則用同樣的方法查下一個字段對應的hash表,就這樣直到查到的value為真實的值為止。另一個通配符查找函數ngx_hash_find_wc_tail這是同樣的原理,不同的是對name從前往后處理每個字段而已。

3 測試

  測試的程序主要是使用nginx實現的數據結構和函數,先通過下列url和ip建立hash,然后對給定的url查找ip。

  (1)測試的數據如下:

 1 static ngx_str_t urls[Max_Num] = {
 2     ngx_string("*.com"),  //220.181.111.147
 3     ngx_string("*.baidu.com.cn"),
 4     ngx_string("*.baidu.com"),
 5     ngx_string(".baidu.com"),
 6     ngx_string("*.google.com"),
 7     ngx_string("www.sina.com.cn"),  //58.63.236.35
 8     ngx_string("www.google.com"),  //74.125.71.105
 9     ngx_string("www.qq.com"),  //60.28.14.190
10     ngx_string("www.163.com"),  //123.103.14.237
11     ngx_string("www.sohu.com"),  //219.234.82.50
12     ngx_string("abo321.org"),  //117.40.196.26
13     ngx_string(".abo321.org"),  //117.40.196.26
14     ngx_string("www.abo321.*")  //117.40.196.26
15 };
16 
17 static ngx_str_t values[Max_Num] = {
18     ngx_string("220.181.111.147"),
19     ngx_string("220.181.111.147"),
20     ngx_string("220.181.111.147"),
21     ngx_string("220.181.111.147"),
22     ngx_string("220.181.111.147"),
23     ngx_string("58.63.236.35"),
24     ngx_string("74.125.71.105"),
25     ngx_string("60.28.14.190"),
26     ngx_string("123.103.14.237"),
27     ngx_string("219.234.82.50"),
28     ngx_string("117.40.196.26"),
29     ngx_string("117.40.196.26"),
30     ngx_string("117.40.196.26")
31 };

  (2)測試代碼為:https://github.com/cc1989/nginx_report/tree/master/nginx_test/hash

  (3)測試結果

 

  1 --------------------------------
  2 create a new pool:
  3 --------------------------------
  4 pool = 0x810d020
  5   .d
  6     .last = 0x810d048
  7     .end = 0x810d420
  8     .next = (nil)
  9     .failed = 0
 10   .max = 984
 11   .current = 0x810d020
 12   .chain = (nil)
 13   .large = (nil)
 14   .cleanup = (nil)
 15   .log = 0x8051518
 16 available pool memory = 984
 17 
 18 --------------------------------
 19 create and add urls to it:
 20 --------------------------------
 21 array = 0xbfed53b4
 22   .elts = 0xb7542008
 23   .nelts = 6
 24   .size = 16
 25   .nalloc = 16384
 26   .pool = 0x810d450
 27   elements:
 28     0xb7542008: {key = ("www.sina.com.cn", 15), key_hash = 1528635686 , value = "58.63.236.35"   }
 29     0xb7542018: {key = ("www.google.com" , 14), key_hash = -702889725 , value = "74.125.71.105"  }
 30     0xb7542028: {key = ("www.qq.com"     , 10), key_hash = 203430122  , value = "60.28.14.190"   }
 31     0xb7542038: {key = ("www.163.com"    , 11), key_hash = -640386838 , value = "123.103.14.237" }
 32     0xb7542048: {key = ("www.sohu.com"   , 12), key_hash = 1313636595 , value = "219.234.82.50"  }
 33     0xb7542058: {key = ("abo321.org"     , 10), key_hash = 1050805370 , value = "117.40.196.26"  }
 34 
 35 array = 0xbfed53cc
 36   .elts = 0xb7501008
 37   .nelts = 4
 38   .size = 16
 39   .nalloc = 16384
 40   .pool = 0x810d450
 41   elements:
 42     0xb7501008: {key = ("com."           , 4 ), key_hash = 0          , value = "220.181.111.147"}
 43     0xb7501018: {key = ("cn.com.baidu."  , 13), key_hash = 0          , value = "220.181.111.147"}
 44     0xb7501028: {key = ("com.baidu."     , 10), key_hash = 0          , value = "220.181.111.147"}
 45     0xb7501038: {key = ("com.google."    , 11), key_hash = 0          , value = "220.181.111.147"}
 46 
 47 array = 0xbfed53e4
 48   .elts = 0xb74c0008
 49   .nelts = 1
 50   .size = 16
 51   .nalloc = 16384
 52   .pool = 0x810d450
 53   elements:
 54     0xb74c0008: {key = ("www.abo321"     , 10), key_hash = 0          , value = "117.40.196.26"  }
 55 
 56 --------------------------------
 57 the pool:
 58 --------------------------------
 59 pool = 0x810d020
 60   .d
 61     .last = 0x810d2a9
 62     .end = 0x810d420
 63     .next = (nil)
 64     .failed = 0
 65   .max = 984
 66   .current = 0x810d020
 67   .chain = (nil)
 68   .large = (nil)
 69   .cleanup = (nil)
 70   .log = 0x8051518
 71 available pool memory = 375
 72 
 73 array = 0xbfed53cc
 74   .elts = 0xb7501008
 75   .nelts = 4
 76   .size = 16
 77   .nalloc = 16384
 78   .pool = 0x810d450
 79   elements:
 80     0xb7501008: {key = ("cn.com.baidu."  , 13), key_hash = 0          , value = "220.181.111.147"}
 81     0xb7501018: {key = ("com."           , 4 ), key_hash = 0          , value = "220.181.111.147"}
 82     0xb7501028: {key = ("com.baidu."     , 10), key_hash = 0          , value = "220.181.111.147"}
 83     0xb7501038: {key = ("com.google."    , 11), key_hash = 0          , value = "220.181.111.147"}
 84 
 85 array = 0xbfed53e4
 86   .elts = 0xb74c0008
 87   .nelts = 1
 88   .size = 16
 89   .nalloc = 16384
 90   .pool = 0x810d450
 91   elements:
 92     0xb74c0008: {key = ("www.abo321"     , 10), key_hash = 0          , value = "117.40.196.26"  }
 93 
 94 --------------------------------
 95 the hash:
 96 --------------------------------
 97 hash = 0xbfed53fc: **buckets = 0x810d2ac, size = 3
 98   0x810d2ac: buckets[0] = 0x810d2c0
 99   0x810d2b0: buckets[1] = 0x810d300
100   0x810d2b4: buckets[2] = 0x810d320
101 
102   key 1528635686: buckets 2: 0x810d320: {value = "58.63.236.35"   , len = 15, name = "www.sina.com.cn"}
103   key -702889725: buckets 1: 0x810d300: {value = "74.125.71.105"  , len = 14, name = "www.google.com" }
104   key 203430122 : buckets 2: 0x810d338: {value = "60.28.14.190"   , len = 10, name = "www.qq.com"     }
105   key -640386838: buckets 0: 0x810d2c0: {value = "123.103.14.237" , len = 11, name = "www.163.com"    }
106   key 1313636595: buckets 0: 0x810d2d4: {value = "219.234.82.50"  , len = 12, name = "www.sohu.com"   }
107   key 1050805370: buckets 2: 0x810d348: {value = "117.40.196.26"  , len = 10, name = "abo321.org"     }
108 value = NULL
109 hash = 0x8111cd0: **buckets = 0x8111cdc, size = 1
110 0x8111cdc: buckets[0] = 0x8111ce0
111 buckets 0: 0x8111ce0: {value = "0x810d3c8       ", len = 2, name = "cn"             }
112     value = NULL
113     hash = 0x810d3c8: **buckets = 0x810d3d4, size = 1
114         0x810d3d4: buckets[0] = 0x810d3e0
115         buckets 0: 0x810d3e0: {value = "0x810d378       ", len = 3, name = "com"            }
116         value = NULL
117         hash = 0x810d378: **buckets = 0x810d384, size = 1
118                 0x810d384: buckets[0] = 0x810d3a0
119                 buckets 0: 0x810d3a0 {value = "220.181.111.147", len = 5, name = "baidu"          }
120 buckets 0: 0x8111ce8: {value = "0x8111c80       ", len = 3, name = "com"            }
121     value = "220.181.111.147"
122     hash = 0x8111c80: **buckets = 0x8111c8c, size = 1
123         0x8111c8c: buckets[0] = 0x8111ca0
124         buckets 0: 0x8111ca0 {value = "220.181.111.147", len = 5, name = "baidu"          }
125         buckets 0: 0x8111cac {value = "220.181.111.147", len = 6, name = "google"         }
126 value = NULL
127 hash = 0x8111d70: **buckets = 0x8111d7c, size = 1
128 0x8111d7c: buckets[0] = 0x8111d80
129 buckets 0: 0x8111d80: {value = "0x8111d20       ", len = 3, name = "www"            }
130     value = NULL
131     hash = 0x8111d20: **buckets = 0x8111d2c, size = 1
132         0x8111d2c: buckets[0] = 0x8111d40
133         buckets 0: 0x8111d40 {value = "117.40.196.26"  , len = 6, name = "abo321"         }
134 
135 --------------------------------
136 the pool:
137 --------------------------------
138 pool = 0x810d020
139   .d
140     .last = 0x810d418
141     .end = 0x810d420
142     .next = 0x8111c70
143     .failed = 0
144   .max = 984
145   .current = 0x810d020
146   .chain = (nil)
147   .large = (nil)
148   .cleanup = (nil)
149   .log = 0x8051518
150 available pool memory = 8
151 
152 pool = 0x8111c70
153   .d
154     .last = 0x8111dc0
155     .end = 0x8112070
156     .next = (nil)
157     .failed = 0
158   .max = 135339148
159   .current = 0x1
160   .chain = 0x810d0d8
161   .large = 0x8111ca0
162   .cleanup = (nil)
163   .log = (nil)
164 available pool memory = 688
165 
166 --------------------------------
167 find test:
168 --------------------------------
169 (url = "*.com"          , key = 40256957   ) found, (ip = "220.181.111.147")
170 (url = "*.baidu.com.cn" , key = 234385007  ) found, (ip = "220.181.111.147")
171 (url = "*.baidu.com"    , key = -724157846 ) found, (ip = "220.181.111.147")
172 (url = ".baidu.com"     , key = 1733355200 ) found, (ip = "220.181.111.147")
173 (url = "*.google.com"   , key = -1465170800) found, (ip = "220.181.111.147")
174 (url = "www.sina.com.cn", key = 1528635686 ) found, (ip = "58.63.236.35   ")
175 (url = "www.google.com" , key = -702889725 ) found, (ip = "74.125.71.105  ")
176 (url = "www.qq.com"     , key = 203430122  ) found, (ip = "60.28.14.190   ")
177 (url = "www.163.com"    , key = -640386838 ) found, (ip = "123.103.14.237 ")
178 (url = "www.sohu.com"   , key = 1313636595 ) found, (ip = "219.234.82.50  ")
179 (url = "abo321.org"     , key = 1050805370 ) found, (ip = "117.40.196.26  ")
180 (url = ".abo321.org"    , key = -4578520   ) not found!
181 (url = "www.abo321.*"   , key = 1494696375 ) found, (ip = "117.40.196.26  ")
182 
183 (url = "*.xx.xx"        , key = 51835370   ) not found!
184 (url = "www.baidu.com"  , key = 270263191  ) found, (ip = "220.181.111.147")
185 (url = "www.baidu."     , key = -239024726 ) not found!
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM