博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
菜鸟nginx源码剖析数据结构篇(三) 单向链表 ngx_list_t[转]
阅读量:5259 次
发布时间:2019-06-14

本文共 5043 字,大约阅读时间需要 16 分钟。

菜鸟nginx源码剖析数据结构篇(三) 单向链表 ngx_list_t

 

  • Author:Echo Chen(陈斌)

  • Email:chenb19870707@gmail.com

  • Blog:

    Date:October 23h, 2014

     

    1.ngx_list优势和特点

     

    ngx_list _t是一个顺序容器,它实际上是动态数组和单向链表的结合体,扩容起来比动态数组简单的多,可以一次扩容一个数组,所以说它结合了 链表插入删除不需要移动的 和 数组下标快速索引 的优势,设计非常让人叫绝,此外它还有以下特点:

    • 链表中存储的元素是灵活的,可以是任何一种数据结构。

    • 链表元素需要占用的内存由ngx_list_t管理,它已经通过数组分配好了。
    • 小块的内存使用链表访问O(n)效率是低下的,可以使用数组通过直接通过偏移量来直接访问O(1)。

    2.源代码位置

     

    头文件:

    源文件:

     

    3.数据结构定义

     

    前面说到ngx_list_t是一个数组链表,链表中的每个结点都是一个数组,ngx_list_part_t 描述的是链表中的一个结点,这个结点又是一个数组,elts为数组首地址,nelts为该数组已经使用的个数,*next为下一个链表结点的指针,定义如下:

     

    1: typedef struct ngx_list_part_s  ngx_list_part_t;
    2: 
    3: //描述链表中的一个结点,这个结点又是一个数组
    4: struct ngx_list_part_s {
    5:     void             *elts;     //首地址
    6:     ngx_uint_t        nelts;    //已经使用的个数
    7:     ngx_list_part_t  *next;     //下一个链表节点的指针
    8: };

     

    ngx_list_t是描述整个链表,其中last 为链表中最后一个数组,part为链表中首个数组,size为数组每个元素占用空间小,nalloc为每个数组可以容纳的元素个数,pool为链表内存池对象,定义如下:

     

     

    1: typedef struct {
    2:     ngx_list_part_t  *last;        //链表中最后一个数组元素
    3:     ngx_list_part_t   part;        //链表中的首个数组元素
    4:     size_t            size;        //每个数组元素占用的空间大小
    5:     ngx_uint_t        nalloc;      //每个数组结点的容量,即每个数组最多可以存放多少个元素
    6:     ngx_pool_t       *pool;        //链表中的内存池对象指针
    7: } ngx_list_t;

     

    其结构如下图所示,最下面一行为内存映像,可以看到整个内存中仅仅多了 一个ngx_list_t 和 几个ngx_list_part_s结果,内存并没有太多的浪费,nginx在内存方面的苛刻确实值得我们学习

     

     

    4.链表创建ngx_list_create和初始化ngx_list_init

     

    1: //链表创建,pool为内存池对象,size为每个数组元素的大小,n为每个数组可以容纳的元素个数
    2: ngx_list_t *ngx_list_create(ngx_pool_t *pool, ngx_uint_t n, size_t size)
    3: {
    4:     ngx_list_t  *list;
    5:    
    6:     //分配ngx_list_t结构
    7:     list = ngx_palloc(pool, sizeof(ngx_list_t));
    8:     if (list == NULL) {
    9:         return NULL;
    10:     }
    11: 
    12:     //初始化
    13:     if (ngx_list_init(list, pool, n, size) != NGX_OK) {
    14:         return NULL;
    15:     }
    16: 
    17:     return list;
    18: }
    19: 
    20: //链表初始化,list为链表结构(在create中创建),pool为内存池,size为每个数组元素的大小,n为每个数组可以容纳的元素个数
    21: static ngx_inline ngx_int_t ngx_list_init(ngx_list_t *list, ngx_pool_t *pool, ngx_uint_t n, size_t size)
    22: {
    23:     //分配一个数组,并用链表首结点指向它
    24:     list->part.elts = ngx_palloc(pool, n * size);
    25:     if (list->part.elts == NULL) {
    26:         return NGX_ERROR;
    27:     }
    28: 
    29:     //已使用个数为0
    30:     list->part.nelts = 0;
    31:     //没有下一个结点,next指为NULL
    32:     list->part.next = NULL;
    33:     //最后一个结点也指向刚刚分配的数组
    34:     list->last = &list->part;
    35:     //数组每个元素大小为size
    36:     list->size = size;
    37:     //数组能容纳元素个数为n
    38:     list->nalloc = n;
    39:     //内存池为pool
    40:     list->pool = pool;
    41: 
    42:     return NGX_OK;
    43: }

     

    可以看到,ngx_list_init调用成功后,会创建一个数组结点。

     

    5.链表添加元素操作ngx_list_push

     

    1: //链表添加元素,l为链表结构
    2: void *ngx_list_push(ngx_list_t *l)
    3: {
    4:     void             *elt;
    5:     ngx_list_part_t  *last;
    6: 
    7:     //last指向链表最后一个结点
    8:     last = l->last;
    9: 
    10:     //如果最后一个结点的数组已经满了
    11:     if (last->nelts == l->nalloc) {
    12: 
    13:         /* the last part is full, allocate a new list part */
    14:        
    15:         //从内存池中分配一个ngx_list_part_t数组对象
    16:         last = ngx_palloc(l->pool, sizeof(ngx_list_part_t));
    17:         if (last == NULL) {
    18:             return NULL;
    19:         }
    20:        
    21:         //从内存池中给数组对象元素分配空间,每个元素大小为size,元素数目为nalloc
    22:         last->elts = ngx_palloc(l->pool, l->nalloc * l->size);
    23:         if (last->elts == NULL) {
    24:             return NULL;
    25:         }
    26: 
    27:         //新分配的数组已使用结点个数为0,下一个链表结点为NULL
    28:         last->nelts = 0;
    29:         last->next = NULL;
    30: 
    31:         //将新分配的数组链到链表,并把链表尾指针指向新分配数组
    32:         l->last->next = last;
    33:         l->last = last;
    34:     }
    35:    
    36:     //elts为数组首地址,elts + size * nelts 即位为分配数组元素的位置
    37:     elt = (char *) last->elts + l->size * last->nelts;
    38:    
    39:     //分配个数+1
    40:     last->nelts++;
    41: 
    42:     return elt;
    43: }
     
    可以看到,push的思想很简单,每次都往链表最后一个数组添加,数组满后就再增加一个数组结点,这里push的时间复杂度为O(1),没有任何遍历操作,充分利用了
    “链表尾插”
     和 “数组下标索引”的优势。 
     

    6.疑问

    源代码中只给了链表的插入操作,没有删除操作,看起来不完整,这里猜测这个链表结构应该不会直接使用,而是被再次封装。

     

    7.实战

     

    看完源代码,这个list设计还是很巧妙的,结合了数组和链表各自的优势,确实不得不让人拍手叫绝。下面尝试给出ngx_list_t的使用例子:

     

     

    1: int main()
    2: {
    3:     ngx_list_t *pList = ngx_list_create( r->pool,4,sizeof( ngx_str_t));
    4:     if( NULL == pList)
    5:     {
    6:         return -1;
    7:     }
    8:    
    9:     ngx_str_t *str = ngx_list_push(pList);
    10:     if( NULL == str)
    11:     {
    12:         return -1;
    13:     }
    14: 
    15:     str->len = sizeof("This is a test for ngx_list_t");
    16:     str->value = "This is a test for ngx_list_t";
    17: 
    18:     //遍历
    19:      ngx_list_part_t *part = &pList->part;
    20:     ngx_str_t *str = part->elts;
    21:    
    22:     for(int i = 0;;i++)
    23:     {
    24:         if( i >= part->nelts)
    25:         {  
    26:             //已经遍历完最后一个数组,没有下一个结点了
    27:             if( NULL == part->next)
    28:             {
    29:                 break;
    30:             }
    31:            
    32:             //还有下一个数组,part指到下一个数组,并把str指到新数组的地址
    33:             part = part->next;
    34:             str = part->elts;
    35:           
    36:             //重置计数器
    37:             i = 0;
    38:          }
    39: 
    40:         //输出结点
    41:         cout << "list elemts :" << str[i] << endl;
    42:        
    43:     }

     

     

转载于:https://www.cnblogs.com/0x2D-0x22/p/4139785.html

你可能感兴趣的文章
hdu 1032 The 3n + 1 problem
查看>>
static关键字
查看>>
转:linux终端常用快捷键
查看>>
009.栈实现队列
查看>>
A-Softmax的总结及与L-Softmax的对比——SphereFace
查看>>
关于软件盘覆盖住布局
查看>>
Unity3D 控制物体移动、旋转、缩放
查看>>
UVa 11059 最大乘积
查看>>
UVa 12545 比特变换器
查看>>
数组分割问题求两个子数组的和差值的小
查看>>
10个著名的思想实验1
查看>>
composer 报 zlib_decode(): data error
查看>>
Elasticsearch——使用_cat查看Elasticsearch状态
查看>>
php curl
查看>>
POJ 1276 Cash Machine(多重背包)
查看>>
SQL笔记
查看>>
优先队列
查看>>
mysql数据库数据乱码解决方案
查看>>
Laravel 安装predis 扩展
查看>>
P5241 序列
查看>>