[nginx源碼分析]hash 和header 回調初始化

整個ngx_http_block中的ngx_conf_parse配置解析完成,,後面主要對配置文件進行優化。

優化可以分爲以下:

1 配置作用域合併

2location劃分

3 http header回調初始化hash

4 初始化http收包回調函數

5 server中的server_name形成hash


http header回調初始化hash

ngx_int_t
  ngx_hash_init(ngx_hash_init_t *hinit,ngx_hash_key_t *names, ngx_uint_t nelts)
  {
     u_char          *elts;
     size_t           len;                                                                                                                                                                                                             
     u_short         *test;
     ngx_uint_t       i, n, key, size,start, bucket_size;
     ngx_hash_elt_t  *elt, **buckets;
  
      for(n = 0; n < nelts; n++) {
         //判斷數組中的節點是否超過bucket_size設置的大小
         if (hinit->bucket_size < NGX_HASH_ELT_SIZE(&names[n]) +sizeof(void *))
         {
             ngx_log_error(NGX_LOG_EMERG, hinit->pool->log, 0,
                            "could notbuild the %s, you should "
                            "increase%s_bucket_size: %i",
                            hinit->name,hinit->name, hinit->bucket_size);
             return NGX_ERROR;
         }
      }
  
     test = ngx_alloc(hinit->max_size * sizeof(u_short),hinit->pool->log);
      if(test == NULL) {
         return NGX_ERROR;
      }
  
     //bucket_size是一個桶的內存大小,把它減去哨兵void*就是真實hash所佔用大小
     bucket_size = hinit->bucket_size - sizeof(void *);
  
      /*
          typedef struct {
             void             *value;
             u_short           len;
             u_char            name[1];
         } ngx_hash_elt_t;
       * 因爲bucket中存放的是一個數組,每個數組元素的結構是上面ngx_hash_elt_t,最小也就是name爲0,最小佔用空間爲2*sizeof(void*)
       *bucket_size/(2*sizeof(void*))是表示每一個bucket 能存放的最大個數
       *nelts/bucket_size/(2*sizeof(void*))表示最少需要多少個bucket
       */
     start = nelts / (bucket_size / (2 * sizeof(void *)));
     start = start ? start : 1;
  
      if(hinit->max_size > 10000 && nelts && hinit->max_size /nelts < 100) {
         start = hinit->max_size - 1000;
      }
  
      for(size = start; size < hinit->max_size; size++) {
  
         //首先清空爲0
         ngx_memzero(test, size * sizeof(u_short));
  
         for (n = 0; n < nelts; n++) {
             if (names[n].key.data == NULL) {
                  continue;
             }
  
             //第一次映射到test[key]時候爲0,後面就加上映射到這塊內存大小
              //test[key]就是記錄使用同一個key佔用的空間數
             key = names[n].key_hash % size;
             test[key] = (u_short) (test[key] + NGX_HASH_ELT_SIZE(&names[n]));
  
  #if 0
              ngx_log_error(NGX_LOG_ALERT,hinit->pool->log, 0,
                            "%ui: %ui %ui\"%V\"",
                            size, key,test[key], &names[n].key);
  #endif
  
             if (test[key] > (u_short) bucket_size) {
                  goto next;
             }
          }
 
         goto found;
  
     next:
  
         continue;
      }
  
     ngx_log_error(NGX_LOG_EMERG, hinit->pool->log, 0,
                    "could not build the%s, you should increase "
                    "either %s_max_size: %ior %s_bucket_size: %i",
                    hinit->name,hinit->name, hinit->max_size,
                    hinit->name,hinit->bucket_size);
  
     ngx_free(test);
  
     return NGX_ERROR;
  
  found:
  
      for(i = 0; i < size; i++) {
         test[i] = sizeof(void *);
      }
  
      for(n = 0; n < nelts; n++) {
         if (names[n].key.data == NULL) {
             continue;
         }
  
         key = names[n].key_hash % size;
         test[key] = (u_short) (test[key] + NGX_HASH_ELT_SIZE(&names[n]));
      }
  
      len= 0;
  
      for(i = 0; i < size; i++) {
         //如果說當前的i沒有映射值,那麼就不給這塊提供內存
         if (test[i] == sizeof(void *)) {
             continue;
         }
  
         test[i] = (u_short) (ngx_align(test[i], ngx_cacheline_size));
         //記錄需要申請的總長度
         len += test[i];
      }
  
      if(hinit->hash == NULL) {
         hinit->hash = ngx_pcalloc(hinit->pool, sizeof(ngx_hash_wildcard_t)
                                               +size * sizeof(ngx_hash_elt_t *));
         if (hinit->hash == NULL) {
             ngx_free(test);
             return NGX_ERROR;
         }
  
         buckets = (ngx_hash_elt_t **)
                        ((u_char *)hinit->hash + sizeof(ngx_hash_wildcard_t));
  
      }else {
         //分配buckets
         buckets = ngx_pcalloc(hinit->pool, size * sizeof(ngx_hash_elt_t *));
         if (buckets == NULL) {
             ngx_free(test);
             return NGX_ERROR;
         }
      }
      //分配元素存儲空間
     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);
  
      for(i = 0; i < size; i++) {
         if (test[i] == sizeof(void *)) {
             continue;
         }
  
         buckets[i] = (ngx_hash_elt_t *) elts;
         //因爲可能有內存對齊操作,所以需要加上該bucket的偏移
         elts += test[i];
  
      }
  
      for(i = 0; i < size; i++) {
         test[i] = 0;
      }
 
                 //下面就是賦值操作
      for(n = 0; n < nelts; n++) {
         if (names[n].key.data == NULL) {
             continue;
         }
  
         key = names[n].key_hash % size;
         elt = (ngx_hash_elt_t *) ((u_char *) buckets[key] + test[key]);
  
         elt->value = names[n].value;
         elt->len = (u_short) names[n].key.len;
  
         ngx_strlow(elt->name, names[n].key.data, names[n].key.len);
  
         test[key] = (u_short) (test[key] + NGX_HASH_ELT_SIZE(&names[n]));
      }
  
      for(i = 0; i < size; i++) {
         if (buckets[i] == NULL) {
             continue;
         }
  
         elt = (ngx_hash_elt_t *) ((u_char *) buckets[i] + test[i]);
  
          elt->value = NULL;
      }
  
     ngx_free(test);
  
     hinit->hash->buckets = buckets;
     hinit->hash->size = size;
 
     return NGX_OK;
  }

ngx_hash_init函數主要是根據傳入的數組ngx_hash_key_t*names和nelts來動態的設置bucket個數,算法是首先獲取每一個節點的最小值,然後bucket_size/(2*sizeof(void*))計算出每一個bucket最大存儲節點個數,然後nelts/bucket_size/(2*sizeof(void*))s算出來的值,就是最小bucket數,然後從這個爲起始值,動態創建bucket個數,一直到hinit->max_size, 滿足bucket的情況是傳入數組映射到相應的bucket,此bucket大小並不超過bucket_size,此時的bucket就滿足要求。然後該函數在計算傳入數組每一個元素映射到相應的內存地址保存在test中。然後申請bucket和bucket相應的起始地址,然後對傳入數組元素設置相應的hash值

ngx_http_init_headers_in_hash函數就是遍歷ngx_http_headers_in數組,生成headers_in數組,代碼如下:

     for (header = ngx_http_headers_in; header->name.len; header++) {
          hk = ngx_array_push(&headers_in);
          if (hk == NULL) {
              return NGX_ERROR;
          }
         
          hk->key = header->name;
          hk->key_hash = ngx_hash_key_lc(header->name.data, header->name.len);
          hk->value = header;
      }

然後調用ngx_hash_init形成hash數組。

整個結構如下:

ngx_http_block對header信息生成hash後,就調用NGX_HTTP_MODULE的回調postconfiguration函數進行模塊函數掛在,此回調函數主要完成模塊的函數掛載。

4 http收包回調函數進行初始化

函數主要把cmcf->phases二維數組,第一維爲11個階段,第二維爲模塊註冊的回調函數,修改爲一維數組,採用next指示,同一個節點的回調可能就執行一個然後就跳到下一個階段具體函數爲ngx_http_init_phase_handlers 函數很簡單就不分析了






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章