菜鸟nginx源代码剖析数据结构篇(七) 哈希表 ngx_hash_t(下)

 

  • Author:Echo Chen(陈斌)

  • Email:chenb19870707@gmail.com

  • Blog:Blog.csdn.net/chen19870707

  • Date:Nov 3rd, 2014

    在前面一篇文章《菜鸟nginx源代码剖析数据结构篇(六) 哈希表 ngx_hash_t(上)》 里介绍了 普通哈希表 和 带有通配符的哈希表 的基本结构和初始化方法,因为篇幅的原因未能解析完结。这篇继续解源代码中剩余的部分。

  • 1.普通哈希表ngx_hash_t查找 ngx_hash_find

    普通哈希表的查找比較简单,思想就是先依据hash值找到相应桶,然后遍历这个桶的每个元素,逐字匹配是否keyword全然同样。全然同样则找到,否则继续,直至找到这个桶的结尾(value = NULL)。

       1: /* @hash 表示哈希表的结构体

       2:  * @key  表示依据哈希方法计算出来的hash值

       3:  * @name 表示实际keyword地址

       4:  * @len  表示实际keyword长度

       5:  */

       6: void *ngx_hash_find(ngx_hash_t *hash, ngx_uint_t key, u_char *name, size_t len)

       7: {

       8:     ngx_uint_t       i;

       9:     ngx_hash_elt_t  *elt;

      10:  

      11:     //依据hash值找到桶索引

      12:     elt = hash->buckets[key % hash->size];

      13:  

      14:     if (elt == NULL) {

      15:         return NULL;

      16:     }

      17:  

      18:     //桶结束的标志为 value 为 NULL

      19:     while (elt->value) {

      20:         //keyword长度不匹配,下一个

      21:         if (len != (size_t) elt->len) {

      22:             goto next;

      23:         }

      24:         

      25:         //遍历keyword每个字符,若所有对得上,则找到,否则有一个不同下一个

      26:         for (i = 0; i < len; i++) {

      27:             if (name[i] != elt->name[i]) {

      28:                 goto next;

      29:             }

      30:         }

      31:  

      32:         return elt->value;

      33:  

      34:     next:

      35:         //从eltkeyword開始向后移动keyword长度个。并行对齐,即为下一个ngx_hash_elt_t

      36:         elt = (ngx_hash_elt_t *) ngx_align_ptr(&elt->name[0] + elt->len,

      37:                                                sizeof(void *));

      38:         continue;

      39:     }

      40:  

      41:     return NULL;

      42: }

  • 2.支持通配符哈希表的前置通配符查找 ngx_hash_find_wc_head

     

    还记得上节在ngx_hash_wildcard_init中,用value指针低2位来携带信息吗?其是有特殊意义的。例如以下图所看到的

    • 00 - value 是 "example.com" 和 "*.example.com"的数据指针
    • 01 - value 不过 "*.example.com"的数据指针
    • 10 - value 是 支持通配符哈希表是 "example.com" 和 "*.example.com" 指针
    • 11 - value 不过 "*.example.com"的指针

    查找的思路就是依据value的指针信息来搜索,源码例如以下:

     

       1: /* @hwc  表示支持通配符的哈希表的结构体

       2:  * @name 表示实际keyword地址

       3:  * @len  表示实际keyword长度

       4:  */

       5: void *ngx_hash_find_wc_head(ngx_hash_wildcard_t *hwc, u_char *name, size_t len)

       6: {

       7:     void        *value;

       8:     ngx_uint_t   i, n, key;

       9:  

      10:     n = len;

      11:     

      12:     //从后往前搜索第一个dot。则n 到 len-1 即为keyword中最后一个 子keyword

      13:     while (n) {

      14:         if (name[n - 1] == '.') {

      15:             break;

      16:         }

      17:  

      18:         n--;

      19:     }

      20:  

      21:     key = 0;

      22:     

      23:     //n 到 len-1 即为keyword中最后一个 子keyword,计算其hash值

      24:     for (i = n; i < len; i++) {

      25:         key = ngx_hash(key, name[i]);

      26:     }

      27:     

      28:     //调用普通查找找到keyword的value(用户自己定义数据指针)

      29:     value = ngx_hash_find(&hwc->hash, key, &name[n], len - n);

      30:  

      31:     /**还记得上节在ngx_hash_wildcard_init中。用value指针低2位来携带信息吗?其是有特殊意义的,例如以下:

      32:      * 00 - value 是 "example.com" 和 "*.example.com"的数据指针

      33:      * 01 - value 不过 "*.example.com"的数据指针

      34:      * 10 - value 是 支持通配符哈希表是 "example.com" 和 "*.example.com" 指针

      35:      * 11 - value 不过 "*.example.com"的指针

      36:      */

      37:     if (value)

      38:     {

      39:  

      40:         if ((uintptr_t) value & 2) {

      41:             

      42:             //搜索到了最后一个子keyword且没有通配符,如"example.com"的example

      43:             if (n == 0) {

      44:                 //value低两位为11,仅为"*.example.com"的指针。这里没有通配符,没招到,返回NULL

      45:                 if ((uintptr_t) value & 1) {

      46:                     return NULL;

      47:                 }

      48:                 

      49:                 //value低两位为10,为"example.com"的指针,value就在下一级的ngx_hash_wildcard_t 的value中,去掉携带的低2位11

      50:                 hwc = (ngx_hash_wildcard_t *)

      51:                                           ((uintptr_t) value & (uintptr_t) ~3);

      52:                 return hwc->value;

      53:             }

      54:             

      55:             //还未搜索完,低两位为11或10,继续去下级ngx_hash_wildcard_t中搜索

      56:             hwc = (ngx_hash_wildcard_t *) ((uintptr_t) value & (uintptr_t) ~3);

      57:             

      58:             //继续搜索 keyword中剩余部分,如"example.com"。搜索 0 到 n -1 即为 example

      59:             value = ngx_hash_find_wc_head(hwc, name, n - 1);

      60:  

      61:             //若找到,则返回

      62:             if (value) 

      63:             {

      64:                 return value;

      65:             }

      66:             

      67:             //低两位为00 找到,即为wc->value

      68:             return hwc->value;

      69:         }

      70:  

      71:         //低两位为01

      72:         if ((uintptr_t) value & 1) 

      73:         {

      74:             //keyword没有通配符,错误返回空

      75:             if (n == 0) 

      76:             {

      77:                 return NULL;

      78:             }

      79:             

      80:             //有通配符,直接返回

      81:             return (void *) ((uintptr_t) value & (uintptr_t) ~3);

      82:         }

      83:  

      84:         //低两位为00。直接返回

      85:         return value;

      86:     }

      87:  

      88:     return hwc->value;

      89: }

     

    3.支持通配符哈希表的后置通配符查找 ngx_hash_find_wc_tail

     

    ngx_hash_find_wc_tail与前置通配符查找差点儿相同,这里value低两位仅有两种标志,更加简单:

    • 00 - value 是指向 用户自己定义数据
    • 11 - value的指向下一个哈希表

      源码例如以下:
       1: void *ngx_hash_find_wc_tail(ngx_hash_wildcard_t *hwc, u_char *name, size_t len)

       2: {

       3:     void        *value;

       4:     ngx_uint_t   i, key;

       5:  

       6:  

       7:     key = 0;

       8:  

       9:     //从前往前搜索第一个dot。则0 到 i 即为keyword中第一个 子keyword

      10:     for (i = 0; i < len; i++)

      11:     {

      12:         if (name[i] == '.')

      13:         {

      14:             break;

      15:         }

      16:         //计算哈希值

      17:         key = ngx_hash(key, name[i]);

      18:     }

      19:  

      20:     //没有通配符,返回NULL

      21:     if (i == len)

      22:     {

      23:         return NULL;

      24:     }

      25:  

      26:     //调用普通查找找到keyword的value(用户自己定义数据指针)

      27:     value = ngx_hash_find(&hwc->hash, key, name, i);

      28:  

      29:  

      30:     /**还记得上节在ngx_hash_wildcard_init中,用value指针低2位来携带信息吗?其是有特殊意义的,例如以下:

      31:      * 00 - value 是数据指针

      32:      * 11 - value的指向下一个哈希表

      33:      */

      34:     if (value)

      35:     {

      36:         //低2位为11,value的指向下一个哈希表,递归搜索

      37:         if ((uintptr_t) value & 2)

      38:         {

      39:  

      40:             i++;

      41:  

      42:             hwc = (ngx_hash_wildcard_t *) ((uintptr_t) value & (uintptr_t) ~3);

      43:  

      44:             value = ngx_hash_find_wc_tail(hwc, &name[i], len - i);

      45:  

      46:             //找到低两位00,返回

      47:             if (value) 

      48:             {

      49:                 return value;

      50:             }

      51:             

      52:             //找打低两位11,返回hwc->value

      53:             return hwc->value;

      54:         }

      55:  

      56:         return value;

      57:     }

      58:  

      59:     //低2位为00。直接返回数据

      60:     return hwc->value;

      61: }

     

     

    4.组合哈希表查找 ngx_hash_find_combined

    组合哈希表的查找思路很easy,先在普通哈希表中查找。没找到再去前置通配符哈希表中查找,最后去后置通配符哈希表中查找。源码例如以下:

       1: void *ngx_hash_find_combined(ngx_hash_combined_t *hash, ngx_uint_t key, u_char *name,size_t len)

       2: {

       3:     void  *value;

       4:     

       5:     //在普通hash表中查找

       6:     if (hash->hash.buckets) {

       7:         value = ngx_hash_find(&hash->hash, key, name, len);

       8:  

       9:         if (value) {

      10:             return value;

      11:         }

      12:     }

      13:  

      14:     if (len == 0) {

      15:         return NULL;

      16:     }

      17:     

      18:     //在前置通配符哈希表中查找

      19:     if (hash->wc_head && hash->wc_head->hash.buckets) {

      20:         value = ngx_hash_find_wc_head(hash->wc_head, name, len);

      21:  

      22:         if (value) {

      23:             return value;

      24:         }

      25:     }

      26:     

      27:     //在后置通配符哈希表中查找

      28:     if (hash->wc_tail && hash->wc_tail->hash.buckets) {

      29:         value = ngx_hash_find_wc_tail(hash->wc_tail, name, len);

      30:  

      31:         if (value) {

      32:             return value;

      33:         }

      34:     }

      35:  

      36:     return NULL;

      37: }

     

     

    5.支持通配符哈希表初始化 ngx_hash_wildcard_init

    首先看一下ngx_hash_wildcard_init 的内存结构,当构造此类型的hash表的时候,实际上是构造了一个hash表的一个“链表”,是通过hash表中的key“链接”起来的。

    比方:对于“*.abc.com”将会构造出2个hash表,第一个hash表中有一个key为com的表项。该表项的value包括有指向第二个hash表的指针。而第二个hash表中有一个表项abc,该表项的value包括有指向*.abc.com相应的value的指针。那么查询的时候,比方查询www.abc.com的时候。先查com。通过查com能够找到第二级的hash表,在第二级hash表中,再查找abc,依次类推,直到在某一级的hash表中查到的表项相应的value相应一个真正的值而非一个指向下一级hash表的指针的时候,查询过程结束。

     

     

    理解了这个,我们就能够看源码了,ngx_hash_wildcard是一个递归函数,递归创建如上图的hash链表,例如以下为凝视版源码。

    精彩的读点有:

    • 因为指针都字节对齐了,低4位肯定为0。这样的操作(name->value = (void *) ((uintptr_t) wdc | (dot ? 3 : 2)) ) 巧妙的使用了指针的低位携带额外信息。节省了内存。让人不得不佩服ngx设计者的想象力。
       1: /*hinit为初始化结构体指针。names为预增加哈希表数组,elts为预增加数组大小
       2: 特别要注意的是这里的key已经都是被预处理过的。
    
    比如:“*.abc.com”或者“.abc.com”被预处理完毕以后。
       3: 变成了“com.abc.”。而“mail.xxx.*”则被预处理为“mail.xxx.”*/
       4: ngx_int_t ngx_hash_wildcard_init(ngx_hash_init_t *hinit, ngx_hash_key_t *names,ngx_uint_t nelts)
       5: {
       6:     size_t                len, dot_len;
       7:     ngx_uint_t            i, n, dot;
       8:     ngx_array_t           curr_names, next_names;
       9:     ngx_hash_key_t       *name, *next_name;
      10:     ngx_hash_init_t       h;
      11:     ngx_hash_wildcard_t  *wdc;
      12:  
      13:     //初始化暂时动态数组curr_names,curr_names是存放当前keyword的数组
      14:     if (ngx_array_init(&curr_names, hinit->temp_pool, nelts,
      15:                        sizeof(ngx_hash_key_t))
      16:         != NGX_OK)
      17:     {
      18:         return NGX_ERROR;
      19:     }
      20:  
      21:     //初始化暂时动态数组next_names,next_names是存放keyword去掉后剩余keyword
      22:     if (ngx_array_init(&next_names, hinit->temp_pool, nelts, sizeof(ngx_hash_key_t)) != NGX_OK)
      23:     {
      24:         return NGX_ERROR;
      25:     }
      26:  
      27:     //遍历 names 数组
      28:     for (n = 0; n < nelts; n = i)
      29:     {
      30:         dot = 0;
      31:  
      32:         //查找 dot
      33:         for (len = 0; len < names[n].key.len; len++)
      34:         {
      35:             if (names[n].key.data[len] == '.')
      36:             {
      37:                 dot = 1;
      38:                 break;
      39:             }
      40:         }
      41:  
      42:         //将keyworddot曾经的keyword放入curr_names
      43:         name = ngx_array_push(&curr_names);
      44:         if (name == NULL) {
      45:             return NGX_ERROR;
      46:         }
      47:  
      48:         name->key.len = len;
      49:         name->key.data = names[n].key.data;
      50:         name->key_hash = hinit->key(name->key.data, name->key.len);
      51:         name->value = names[n].value;
      52:  
      53:         dot_len = len + 1;
      54:  
      55:         //len指向dot后剩余keyword
      56:         if (dot)
      57:         {
      58:             len++;
      59:         }
      60:  
      61:         next_names.nelts = 0;
      62:  
      63:         //假设names[n] dot后还有剩余keyword。将剩余keyword放入next_names中
      64:         if (names[n].key.len != len)
      65:         {
      66:             next_name = ngx_array_push(&next_names);
      67:             if (next_name == NULL) {
      68:                 return NGX_ERROR;
      69:             }
      70:  
      71:             next_name->key.len = names[n].key.len - len;
      72:             next_name->key.data = names[n].key.data + len;
      73:             next_name->key_hash = 0;
      74:             next_name->value = names[n].value;
      75:  
      76:         }
      77:  
      78:         //假设上面搜索到的keyword没有dot,从n+1遍历names,将keyword比它长的所有放入next_name
      79:         for (i = n + 1; i < nelts; i++)
      80:         {
      81:             //前len个keyword同样
      82:             if (ngx_strncmp(names[n].key.data, names[i].key.data, len) != 0) {
      83:                 break;
      84:             }
      85:  
      86:  
      87:             if (!dot
      88:                 && names[i].key.len > len
      89:                 && names[i].key.data[len] != '.')
      90:             {
      91:                 break;
      92:             }
      93:  
      94:             next_name = ngx_array_push(&next_names);
      95:             if (next_name == NULL) {
      96:                 return NGX_ERROR;
      97:             }
      98:  
      99:             next_name->key.len = names[i].key.len - dot_len;
     100:             next_name->key.data = names[i].key.data + dot_len;
     101:             next_name->key_hash = 0;
     102:             next_name->value = names[i].value;
     103:  
     104:         }
     105:  
     106:         //假设next_name非空
     107:         if (next_names.nelts)
     108:         {
     109:             h = *hinit;
     110:             h.hash = NULL;
     111:  
     112:             //递归。创建一个新的哈西表
     113:             if (ngx_hash_wildcard_init(&h, (ngx_hash_key_t *) next_names.elts,next_names.nelts) != NGX_OK)
     114:             {
     115:                 return NGX_ERROR;
     116:             }
     117:  
     118:             wdc = (ngx_hash_wildcard_t *) h.hash;
     119:             
     120:             //如上图,将用户value值放入新的hash表
     121:             if (names[n].key.len == len)
     122:             {
     123:                 wdc->value = names[n].value;
     124:             }
     125:             
     126:             //并将当前value值指向新的hash表
     127:             name->value = (void *) ((uintptr_t) wdc | (dot ? 3 : 2));
     128:  
     129:         } else if (dot)
     130:         {
     131:             name->value = (void *) ((uintptr_t) name->value | 1);
     132:         }
     133:     }
     134:  
     135:     //将最外层hash初始化
     136:     if (ngx_hash_init(hinit, (ngx_hash_key_t *) curr_names.elts,curr_names.nelts) != NGX_OK)
     137:     {
     138:         return NGX_ERROR;
     139:     }
     140:  
     141:     return NGX_OK;
     142: }

     

    6. 哈希键数组初始化 ngx_hash_keys_array_init

     

    初始化ngx_hash_keys_arrays_t 结构体。type的取值范围仅仅有两个,NGX_HASH_SMALL表示初始化元素较少,NGX_HASH_LARGE表示初始化元素较多。在向ha中增加时必须调用此方法

       1: ngx_int_t ngx_hash_keys_array_init(ngx_hash_keys_arrays_t *ha, ngx_uint_t type)

       2: {

       3:     ngx_uint_t  asize;

       4:  

       5:     if (type == NGX_HASH_SMALL)

       6:     {

       7:         asize = 4;

       8:         ha->hsize = 107;

       9:     }

      10:     else

      11:     {

      12:         asize = NGX_HASH_LARGE_ASIZE;

      13:         ha->hsize = NGX_HASH_LARGE_HSIZE;

      14:     }

      15:  

      16:     //初始化 存放非通配符keyword的数组

      17:     if (ngx_array_init(&ha->keys, ha->temp_pool, asize, sizeof(ngx_hash_key_t)) != NGX_OK)

      18:     {

      19:         return NGX_ERROR;

      20:     }

      21:  

      22:     //初始化 存放前置通配符处理好的keyword 数组

      23:     if (ngx_array_init(&ha->dns_wc_head, ha->temp_pool, asize, sizeof(ngx_hash_key_t)) != NGX_OK)

      24:     {

      25:         return NGX_ERROR;

      26:     }

      27:  

      28:     //初始化 存放后置通配符处理好的keyword 数组

      29:     if (ngx_array_init(&ha->dns_wc_tail, ha->temp_pool, asize, sizeof(ngx_hash_key_t))!= NGX_OK)

      30:     {

      31:         return NGX_ERROR;

      32:     }

      33:  

      34:     /*初始化 二位数组 ,这个数组存放的第一个维度代表的是bucket的编号,

      35:       那么keys_hash[i]中存放的是全部的key算出来的hash值对hsize取模以后的值为i的key。
    

      36:       如果有3个key,各自是key1,key2和key3如果hash值算出来以后对hsize取模的值都是i,

      37:       那么这三个key的值就顺序存放在keys_hash[i][0],keys_hash[i][1], keys_hash[i][2]。

      38:       该值在调用的过程中用来保存和检測是否有冲突的key值。也就是是否有反复。*/

      39:     ha->keys_hash = ngx_pcalloc(ha->temp_pool, sizeof(ngx_array_t) * ha->hsize);

      40:     if (ha->keys_hash == NULL)

      41:     {

      42:         return NGX_ERROR;

      43:     }

      44:  

      45:     // 该数组在调用的过程中用来保存和检測是否有冲突的前向通配符的key值,也就是是否有反复。
    

      46:     ha->dns_wc_head_hash = ngx_pcalloc(ha->temp_pool,sizeof(ngx_array_t) * ha->hsize);

      47:  

      48:     if (ha->dns_wc_head_hash == NULL)

      49:     {

      50:         return NGX_ERROR;

      51:     }

      52:  

      53:     // 该数组在调用的过程中用来保存和检測是否有冲突的后向通配符的key值,也就是是否有反复。
    

      54:     ha->dns_wc_tail_hash = ngx_pcalloc(ha->temp_pool, sizeof(ngx_array_t) * ha->hsize);

      55:     if (ha->dns_wc_tail_hash == NULL)

      56:     {

      57:         return NGX_ERROR;

      58:     }

      59:  

      60:     return NGX_OK;

      61: }

     

    7. 向ngx_hash_keys_array中加入keyword

     

       1: ngx_int_t ngx_hash_keys_array_init(ngx_hash_keys_arrays_t *ha, ngx_uint_t type)

       2: {

       3:     ngx_uint_t  asize;

       4:  

       5:     if (type == NGX_HASH_SMALL)

       6:     {

       7:         asize = 4;

       8:         ha->hsize = 107;

       9:     }

      10:     else

      11:     {

      12:         asize = NGX_HASH_LARGE_ASIZE;

      13:         ha->hsize = NGX_HASH_LARGE_HSIZE;

      14:     }

      15:  

      16:     //初始化 存放非通配符keyword的数组

      17:     if (ngx_array_init(&ha->keys, ha->temp_pool, asize, sizeof(ngx_hash_key_t)) != NGX_OK)

      18:     {

      19:         return NGX_ERROR;

      20:     }

      21:  

      22:     //初始化 存放前置通配符处理好的keyword 数组

      23:     if (ngx_array_init(&ha->dns_wc_head, ha->temp_pool, asize, sizeof(ngx_hash_key_t)) != NGX_OK)

      24:     {

      25:         return NGX_ERROR;

      26:     }

      27:  

      28:     //初始化 存放后置通配符处理好的keyword 数组

      29:     if (ngx_array_init(&ha->dns_wc_tail, ha->temp_pool, asize, sizeof(ngx_hash_key_t))!= NGX_OK)

      30:     {

      31:         return NGX_ERROR;

      32:     }

      33:  

      34:     /*初始化 二位数组 ,这个数组存放的第一个维度代表的是bucket的编号。

      35:       那么keys_hash[i]中存放的是全部的key算出来的hash值对hsize取模以后的值为i的key。

      36:       如果有3个key,各自是key1,key2和key3如果hash值算出来以后对hsize取模的值都是i,

      37:       那么这三个key的值就顺序存放在keys_hash[i][0],keys_hash[i][1], keys_hash[i][2]。

      38:       该值在调用的过程中用来保存和检測是否有冲突的key值。也就是是否有反复。
    
    */

      39:     ha->keys_hash = ngx_pcalloc(ha->temp_pool, sizeof(ngx_array_t) * ha->hsize);

      40:     if (ha->keys_hash == NULL)

      41:     {

      42:         return NGX_ERROR;

      43:     }

      44:  

      45:     // 该数组在调用的过程中用来保存和检測是否有冲突的前向通配符的key值。也就是是否有反复。

      46:     ha->dns_wc_head_hash = ngx_pcalloc(ha->temp_pool,sizeof(ngx_array_t) * ha->hsize);

      47:  

      48:     if (ha->dns_wc_head_hash == NULL)

      49:     {

      50:         return NGX_ERROR;

      51:     }

      52:  

      53:     // 该数组在调用的过程中用来保存和检測是否有冲突的后向通配符的key值,也就是是否有反复。
    

      54:     ha->dns_wc_tail_hash = ngx_pcalloc(ha->temp_pool, sizeof(ngx_array_t) * ha->hsize);

      55:     if (ha->dns_wc_tail_hash == NULL)

      56:     {

      57:         return NGX_ERROR;

      58:     }

      59:  

      60:     return NGX_OK;

      61: }

    -

    Echo Chen:Blog.csdn.net/chen19870707

    -

    菜鸟nginx源代码剖析数据结构篇(七) 哈希表 ngx_hash_t(下)的更多相关文章

    1. 菜鸟nginx源代码剖析数据结构篇(一)动态数组ngx_array_t

      菜鸟nginx源代码剖析数据结构篇(一)动态数组ngx_array_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.csd ...

    2. 菜鸟nginx源代码剖析数据结构篇(八) 缓冲区链表ngx_chain_t

      菜鸟nginx源代码剖析数据结构篇(八) 缓冲区链表 ngx_chain_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog. ...

    3. 菜鸟nginx源代码剖析数据结构篇(十) 自旋锁ngx_spinlock

      菜鸟nginx源代码剖析数据结构篇(十) 自旋锁ngx_spinlock Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.cs ...

    4. 菜鸟nginx源代码剖析数据结构篇(六) 哈希表 ngx_hash_t(上)

      菜鸟nginx源代码剖析数据结构篇(六) 哈希表 ngx_hash_t(上) Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog. ...

    5. 菜鸟nginx源代码剖析数据结构篇(九) 内存池ngx_pool_t

      菜鸟nginx源代码剖析数据结构篇(九) 内存池ngx_pool_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.csdn ...

    6. 菜鸟nginx源代码剖析 框架篇(一) 从main函数看nginx启动流程

      菜鸟nginx源代码剖析 框架篇(一) 从main函数看nginx启动流程 Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.c ...

    7. 新秀nginx源代码分析数据结构篇(四)红黑树ngx_rbtree_t

      新秀nginx源代码分析数据结构篇(四)红黑树ngx_rbtree_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.csd ...

    8. 新秀nginx源代码分析数据结构篇(两) 双链表ngx_queue_t

      nginx源代码分析数据结构篇(两) 双链表ngx_queue_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.csdn. ...

    9. 菜鸟nginx源代码剖析 配置与部署篇(一) 手把手实现nginx &quot;I love you&quot;

      菜鸟nginx源代码剖析 配置与部署篇(一) 手把手配置nginx "I love you" Author:Echo Chen(陈斌) Email:chenb19870707@gm ...

    随机推荐

    1. Random numbers

      Most computer programs do the same thing every time they execute, given the same inputs, so they are ...

    2. kali 2.0 linux中的Nmap的主机探测

      不多说,直接上干货! 如果是第一次接触Nmap,推荐在MSF终端中输入不加任何参数的Nmap命令,以查看其使用方法. 更多,其实, msf > nmap -h [*] exec: nmap -h ...

    3. 51Nod 最长的循环节(打表预处理)

      正整数k的倒数1/k,写为10进制的小数如果为无限循环小数,则存在一个循环节,求<=n的数中,倒数循环节长度最长的那个数,假如存在多个最优的答案,输出所有答案中最大的那个数.     1/6= ...

    4. vue打包后显示空白正确处理方法

      vue打包后显示空白正确处理方法是 1.找到配置文件(js与css加载不上) 修改 这样打包处理可以打开但是页面样式会找不到 2.修改(针对css中的图片加载不上) 找到对应的位置加上publicPa ...

    5. [洛谷P2394]yyy loves Chemistry I

      题目大意:给你一个实数x($0<x\leq 1$),要你求x/23的值(保留8位小数). 解题思路:此题用double读的精度是不够的,用long double直接读入也会WA,正确做法是“sc ...

    6. 【APP自动化】Appium Android 元素定位方法 原生+H5

      参考资料: http://blog.csdn.net/vivian_ljx/article/details/54410024

    7. Unity 如何将apk放到Android系统的system里

      有时我们需要用unity开发一款Android的系统软件,很坑,步骤如下: 1.用unity打包出来,签名. 2.用解压工具打开签过名的apk. 3.将lib里面的.so文件复制出来. 4.adb r ...

    8. cigarette

      #include<iostream> using namespace std; int main() { int N; cin>>N; while(N--) { int Who ...

    9. LeetCode -- 最大连续乘积子序列

      问题描写叙述: 给定数组,找出连续乘积最大值的子序列.比如 0,-1,-3.-2.则最大连续乘积为6= (-3) * (-2) 实现思路此题与最大连续和的子序列问题相似,也可通过找到递推公式然后用DP ...

    10. UITextField监控文字变化方法

      项目里须要在不隐藏键盘的情况下.来更改button 的状态. 这里就是要对uitextfield监控.uitextfield继承于UIControl.所以能够 使用addTarget方法.详细例如以下 ...