菜鸟nginx源码剖析数据结构篇(三) 单向链表 ngx_list_t[转]
菜鸟nginx源码剖析数据结构篇(三) 单向链表 ngx_list_t
Author:Echo Chen(陈斌)
Email:chenb19870707@gmail.com
Blog:Blog.csdn.net/chen19870707
Date:October 23h, 2014
1.ngx_list优势和特点
ngx_list _t是一个顺序容器,它实际上是动态数组和单向链表的结合体,扩容起来比动态数组简单的多,可以一次扩容一个数组,所以说它结合了 链表插入删除不需要移动的 和 数组下标快速索引 的优势,设计非常让人叫绝,此外它还有以下特点:
链表中存储的元素是灵活的,可以是任何一种数据结构。
- 链表元素需要占用的内存由ngx_list_t管理,它已经通过数组分配好了。
- 小块的内存使用链表访问O(n)效率是低下的,可以使用数组通过直接通过偏移量来直接访问O(1)。
2.源代码位置
头文件:http://trac.nginx.org/nginx/browser/nginx/src/core/ngx_list.h
源文件:http://trac.nginx.org/nginx/browser/nginx/src/core/ngx_list.c
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: }
菜鸟nginx源码剖析数据结构篇(三) 单向链表 ngx_list_t[转]的更多相关文章
- 菜鸟nginx源码剖析数据结构篇(九) 内存池ngx_pool_t[转]
菜鸟nginx源码剖析数据结构篇(九) 内存池ngx_pool_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.csdn. ...
- 菜鸟nginx源码剖析数据结构篇(七) 哈希表 ngx_hash_t(下)[转]
菜鸟nginx源码剖析数据结构篇(七) 哈希表 ngx_hash_t(下) Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.c ...
- 菜鸟nginx源码剖析数据结构篇(六) 哈希表 ngx_hash_t(上)[转]
菜鸟nginx源码剖析数据结构篇(六) 哈希表 ngx_hash_t(上) Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.c ...
- 菜鸟nginx源码剖析数据结构篇(十一) 共享内存ngx_shm_t[转]
菜鸟nginx源码剖析数据结构篇(十一) 共享内存ngx_shm_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.csdn ...
- 菜鸟nginx源码剖析数据结构篇(十) 自旋锁ngx_spinlock[转]
菜鸟nginx源码剖析数据结构篇(十) 自旋锁ngx_spinlock Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.csd ...
- 菜鸟nginx源码剖析数据结构篇(八) 缓冲区链表ngx_chain_t[转]
菜鸟nginx源码剖析数据结构篇(八) 缓冲区链表 ngx_chain_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.c ...
- 菜鸟nginx源码剖析数据结构篇(五) 基数树 ngx_radix_tree_t[转]
菜鸟nginx源码剖析数据结构篇(五) 基数树 ngx_radix_tree_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blo ...
- 菜鸟nginx源码剖析数据结构篇(四)红黑树ngx_rbtree_t[转]
菜鸟nginx源码剖析数据结构篇(四)红黑树ngx_rbtree_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.csdn ...
- 菜鸟nginx源码剖析数据结构篇(一)动态数组ngx_array_t[转]
菜鸟nginx源码剖析数据结构篇(一)动态数组ngx_array_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.csdn ...
随机推荐
- 通过Matlab SDK 获取tushare数据
概要说明 版本要求:Matlab需要2016b及以上版本 接口说明:可以用help pro_api和help pro_bar查看 demo程序:请参考tushare_pro_test.m文件 程序包下 ...
- LA 3263 /// 欧拉定理 oj21860
题目大意: n个端点的一笔画 第n个和第1个重合 即一笔画必定是闭合曲线 输出平面被分成的区域数 欧拉定理 V+F-E=2 即 点数+面数-边数=2 (这里的面数包括了外部) #include < ...
- 基于第三方开源库的OPC服务器开发指南(1)——OPC与DCOM
事儿太多,好多事情并不以我的意志为转移,原想沉下心好好研究.学习图像识别,继续丰富我的机器视觉库,并继续<机器视觉及图像处理系列>博文的更新,但计划没有变化快,好多项目要完成,只好耽搁下来 ...
- 多个for循环使用
for循环 例子 语法 vue.js的for循环 <div id="myfor"><li v-for="student in studentList&q ...
- list集合排序2
java根据List内对象的属性排序 原创 2016年12月07日 00:20:01 标签: java / 对象 / sort / compare 2625 方法一:实现Comparator接口,并重 ...
- MySQL数据库之DQL(数据查询语言)
1.MySQL之DQL查询AS CONCAT LIKE的使用 (1)select 列名1,列名2,...... from 表名 [where 条件] 查询所有字段用*,不带where条件的话,就会把表 ...
- [NOIP2005] 过河【Dp,思维题,缩点】
Online Judge:Luogu P1052 Label:Dp,思维题,缩点,数学 题目描述 在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧.在桥上有一些石子,青蛙很讨厌踩在这些石子 ...
- note : Get FilePathName from FILE_OBJECT
转自:http://blog.csdn.net/lostspeed/article/details/11738311 封了一个函数, 从 FILE_OBJECT 中 得到 FilePathName 在 ...
- thinkphp rabc权限总结
今天晚上把ThinkPHP的权限分配弄明白了,心里的包袱立刻放下了,感觉那个爽啊!稍微记录一下. 背景:CMS系统开发(17do). 项目分组:Admin(后台管理).Home(前台显示). Admi ...
- Erlang学习记录:输入和输出
输入和输出 输入和输出功能都被定义在io模块 输出功能非常常用,由于erlang项目没有可断点调试的IDE(或者说根本不需要),所以所有的调试操作都是由io输出 来调试的 io:get_line/1. ...