Nginx源代码分析--基本数据结构--hash

    技术2022-05-20  45

    咱们来看这段代码:

     

     

    //这段让人欢喜让人忧的宏定义

     

    //先说说有什么需求:

     

             //桶的大小是有限制的,在ngx_hash_init_t 数据结构中有bucket_size这个值。

             //因此,针对桶的每个k-v对的分配内存的大小和附属的数据机构中元素占用的内存的加和值,必须小于这个值。

             //否则如果加和值,大于bucket_size,则该k-v对无法分配到桶,因为没有这样大小的桶容纳这个k-v对所需要的数据结构。

     

    //这段宏的目的是,计算每个需要存储到bucket的k-v对的分配内存字节数,以便之后进行上述加和值的计算,并与bucket_size比较,以判断//是否合法。

     

    //由于name这个k-v对中,会有key的值存储到bucket,而key的长度,即key.size_t不占空间,另外key_hash也不占空间,这都是分配过

    //程中需要用的,void* value是占内存的。所以整个k-v对数据占用的内存大小是key.len大小的key的数值经过粒度为sizeof(void*)对齐

    //后,加上void *value的占用内存大小。

     

     

    //当然,只要分配到bucket键值对,还必须有一个该bucket键值对结束表示,即void *value=NULL。这个在与bucket_size比较之前要加

    //上,参见后述代码

     

    #define NGX_HASH_ELT_SIZE(name)                                               /    (sizeof(void *) + ngx_align((name)->key.len + 2, sizeof(void *)))

     

     

    ngx_int_tngx_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;    //对每个k-v键值对,都要进行检测

     

        for (n = 0; n < nelts; n++) {

     

            //要加上sizeof(void *),因为bucket最后需要k-v对结束标志,是void * value来做的。

            if (hinit->bucket_size < NGX_HASH_ELT_SIZE(&names[n]) + sizeof(void *))        {            ngx_log_error(NGX_LOG_EMERG, hinit->pool->log, 0,                          "could not build the %s, you should "                          "increase %s_bucket_size: %i",                          hinit->name, hinit->name, hinit->bucket_size);            return NGX_ERROR;        }    }

     

       //估计给这个内存池,取名test的意思是下边要“看看大概需要多少个桶"。这个老毛子英语看来也不咋地,顶多6级,因为想法跟我一样。

     

       //分配最大大小即,hinit->max_size指定大小的内存池。

        test = ngx_alloc(hinit->max_size * sizeof(u_short), hinit->pool->log);    if (test == NULL) {        return NGX_ERROR;    }

     

       //这里为什么要-sizeof(void *)呢?还是那个原因,我得预留一个当标识啊。其他的键值对可不能占用我这个标识,嘿嘿~~

      //这个计算是为了之后做决定。给一个bucket分配的多个k-v对占用的空间是不能大于这个值的,因为大于了则加上末尾表示void  

      //*==NULL占用的空间后,就大于bucket_size了。这种情况下,需要把这个k-v值放到其他桶。其实也说明,当前的桶数目不足。参见后边 

      //代码

        bucket_size = hinit->bucket_size - sizeof(void *);

     

      //这个老毛子,又开始耍小聪明了:预测一下。这个家伙觉得,需要的桶数目小于start数目的可能性太小了,所以应该从start开始试。star的 

      //值是如下计算的:

      //是k-v对的个数,除以bucket_size/(2*sizeof(void *))的个数。

      //当然如果是小数(start=0)则应该变1,桶个数不能是0吧,嘿嘿。

     

        start = nelts / (bucket_size / (2 * sizeof(void *)));

     

     

        start = start ? start : 1;

     

       //老毛子还觉得。。。。。。

        if (hinit->max_size > 10000 && hinit->max_size / nelts < 100) {        start = hinit->max_size - 1000;    }

     

     

       //最后终于得出个,他认为的小于这个值可能性很小的值,即start,并开始从这个值试验,到底需要多少个桶。

       //这个就是试验过程了。

     

        for (size = start; size < hinit->max_size; size++) {

     

             //老毛子每次试验桶多少合适,和每个桶里有多少k-v对,总是用test的前几个存储这些值。不用这么小气吧,整个局部变量又不浪费内

            //存。

            //这里是每次都把存储每个桶k-v值的单元清0        ngx_memzero(test, size * sizeof(u_short));        for (n = 0; n < nelts; n++) {            if (names[n].key.data == NULL) {                continue;            }            //这个就是计算分配到哪个桶。size是预测的应该有多少个桶。

                key = names[n].key_hash % size;

     

                //分配的这个桶的内存数目应该加上对齐后内存数目。

     

                test[key] = (u_short) (test[key] + NGX_HASH_ELT_SIZE(&names[n]));

     

     

    //和Log级别有关,如果0 则打印log,否则不打印#if 0            ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0,                          "%ui: %ui %ui /"%V/"",                          size, key, test[key], &names[n].key);#endif

     

                //这里终于用到了bucket_size,大于这个值,则说明这个size不合适啊goto next,调整下桶的数目

                if (test[key] > (u_short) bucket_size) {                goto next;            }        }

     

            //如果都满足了,哈哈,这个数目就足够了。跳转到found去处理吧。起个名字叫finish也好啊。英语水平果然和我差不多。

            goto found;    next:        continue;    }


    最新回复(0)