Content
1.链表结构
1.2 ngx_list_t的逻辑结构
2.1创建链表
3.一个例子
3.2如何编译
4.小结
0. 序
本文继续介绍nginx的容器——链表。
链表实现文件:文件:./src/core/ngx_list.h/.c。.表示nginx-1.0.4代码目录,本文为/usr/src/nginx-1.0.4。
1. 链表结构
1.1 ngx_list_t结构
nginx的链表(头)结构为ngx_list_t,链表节点结构为ngx_list_part_t,定义如下。
- typedef struct ngx_list_part_s ngx_list_part_t;
- struct ngx_list_part_s { //链表节点结构
- void *elts; //指向该节点实际的数据区(该数据区中可以存放nalloc个大小为size的元素)
- ngx_uint_t nelts; //实际存放的元素个数
- ngx_list_part_t *next; //指向下一个节点
- };
- typedef struct{ //链表头结构
- ngx_list_part_t *last; //指向链表最后一个节点(part)
- ngx_list_part_t part; //链表头中包含的第一个节点(part)
- size_t size; //每个元素大小
- ngx_uint_t nalloc; //链表所含空间个数,即实际分配的小空间的个数
- ngx_pool_t *pool; //该链表节点空间在此内存池中分配
- }ngx_list_t;
其中,sizeof(ngx_list_t)=28,sizeof(ngx_list_part_t)=12。
由此可见,nginx的链表也要从内存池中分配。对于每一个节点(list part)将分配nalloc个大小为size的小空间,实际分配的大小为(nalloc * size)。详见下文的分析。
1.2 ngx_list_t的逻辑结构
ngx_list_t结构引用了ngx_pool_t结构,因此本文参考nginx-1.0.4源码分析—内存池结构ngx_pool_t及内存管理一文画出相关结构的逻辑图,如下。注:本文采用UML的方式画出该图。
2. 链表操作
链表操作共3个,如下。
- //创建链表
- ngx_list_t*ngx_list_create(ngx_pool_t *pool, ngx_uint_t n, size_t size);
- //初始化链表
- static ngx_inline ngx_int_t ngx_list_init(ngx_list_t *list, ngx_pool_t *pool,
- ngx_uint_tn, size_t size);
- //添加元素
- void*ngx_list_push(ngx_list_t *l)
他们的实现都很简单,本文只分析创建链表和添加元素操作。
2.1创建链表
创建链表的操作实现如下,首先分配链表头(28B),然后分配头节点(即链表头中包含的part)数据区,两次分配均在传入的内存池(pool指向的内存池)中进行。然后简单初始化链表头并返回链表头的起始位置。
- ngx_list_t *
- ngx_list_create(ngx_pool_t*pool, ngx_uint_t n, size_t size)
- {
- ngx_list_t *list;
- list = ngx_palloc(pool,sizeof(ngx_list_t)); //从内存池中分配链表头
- if (list == NULL) {
- return NULL;
- }
- list->part.elts =ngx_palloc(pool, n * size); //接着分配n*size大小的区域作为链表数据区
- if (list->part.elts == NULL) {
- return NULL;
- }
- list->part.nelts = 0; //初始化
- list->part.next = NULL;
- list->last = &list->part;
- list->size = size;
- list->nalloc = n;
- list->pool = pool;
- return list; //返回链表头的起始位置
- }
2.2添加元素
添加元素操作实现如下,同nginx数组实现类似,其实际的添加操作并不在该函数中完成。函数ngx_list_push返回可以在该链表数据区中放置元素(元素可以是1个或多个)的位置,而添加操作即在获得添加位置之后进行,如后文的例子。
- void *
- ngx_list_push(ngx_list_t*l)
- {
- void *elt;
- ngx_list_part_t *last;
- last = l->last;
- if (last->nelts ==l->nalloc) { //链表数据区满
- /* the last part is full, allocate anew list part */
- last =ngx_palloc(l->pool, sizeof(ngx_list_part_t)); //分配节点(list part)
- if (last == NULL) {
- return NULL;
- }
- last->elts =ngx_palloc(l->pool, l->nalloc * l->size);//分配该节点(part)的数据区
- if (last->elts == NULL) {
- return NULL;
- }
- last->nelts = 0;
- last->next = NULL;
- l->last->next =last; //将分配的list part插入链表
- l->last = last; //并修改list头的last指针
- }
- elt = (char *)last->elts + l->size * last->nelts; //计算下一个数据在链表数据区中的位置
- last->nelts++; //实际存放的数据个数加1
- return elt; //返回该位置
- }
由此可见,向链表中添加元素实际上就是从内存池中分配链表节点(part)及其该节点的实际数据区,并修改链表节点(part)信息。
注1:与数组的区别,数组数据区满时要扩充数据区空间;而链表每次要分配节点及其数据区。
注2:链表的每个节点(part)的数据区中可以放置1个或多个元素,这里的元素可以是一个整数,也可以是一个结构。
下图是一个有3个节点的链表的逻辑结构图。
图中的线太多,容易眼晕,下面这个图可能好一些。
转自:http://blog.csdn.net/livelylittlefish/article/details/6599065