一、Nginx中链表(ngx_list_t)的结构图
ngx_list_t
作为一个链表的结构体,里面是一些链表的信息,ngx_list_part_t
是链表的节点。
ngx中的链表并不是将内存简单串起来,而是将内存组织成块(chunck,在ngx中叫part),然后将这些块通过链表串起来。
每个块划分为nalloc
片区域,每片区域的大小为size
,因此一个chunck的大小为n*nalloc
。
一个chunck中已分配的区域片数为nelts
。
二、源码阅读
1、ngx_list_part_s
//一个chunk块(part) struct ngx_list_part_s { void *elts; //数据区域首地址的指针 ngx_uint_t nelts; //已分配的数据区域的个数 ngx_list_part_t *next; //下一个chunck(part) };
2、ngx_list_t
链表的结构体,包含一些链表的信息,用于将(chunck)part组织起来
//链表,用来组织chunk块 typedef struct { ngx_list_part_t *last; //最后一块chunck ngx_list_part_t part; //第一块chunck size_t size; //一个片的大小为size(一个chunck分为nalloc片,那么一个chunck大小为nalloc*size) ngx_uint_t nalloc; //一个chunck划分成nalloc片区域 ngx_pool_t *pool; //所属的内存池 } ngx_list_t;
3、ngx_list_create
创建一个链表,首先要从内存池中分配一块数据给list结构体,然后再交给ngx_list_init去初始化具体信息,和分配数据区域空间。
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));//从内存池分配一块数据,用于存储list结构体 if (list == NULL) { return NULL; } if (ngx_list_init(list, pool, n, size) != NGX_OK) {//初始化 return NULL; } return list; }
4、ngx_list_init
初始化list结构体的信息,并分配一块chunck
static ngx_inline ngx_int_t ngx_list_init(ngx_list_t *list, ngx_pool_t *pool, ngx_uint_t n, size_t size) { list->part.elts = ngx_palloc(pool, n * size);//创建一块chunck的数据区域,大小为 片数量*每个片的空间大小 if (list->part.elts == NULL) { return NGX_ERROR; } list->part.nelts = 0;//初始化的时候,还没将空间使用,因此是0 list->part.next = NULL; list->last = &list->part;//初始化的时候最后一块chunck就是第一块 list->size = size;//初始化的数据大小 list->nalloc = n; list->pool = pool;//所属的内存池 return NGX_OK; }
5、ngx_list_push
通过list来获取一块空间的首地址。
取一块数据为什么叫push呢?因为数据是通过list组织起来的,分配的空间通过list来组织,也就是要push到list中的。
void * ngx_list_push(ngx_list_t *l) { void *elt; ngx_list_part_t *last; last = l->last;//list中最后一个chunck if (last->nelts == l->nalloc) {//如果最后一块chunck中的片已经全部使用完了,那么就要新创建一个chunck,以及空间大小 /* the last part is full, allocate a new list part */ last = ngx_palloc(l->pool, sizeof(ngx_list_part_t));//创建chunck头的空间 if (last == NULL) { return NULL; } last->elts = ngx_palloc(l->pool, l->nalloc * l->size);//创建chunck数据空间 if (last->elts == NULL) { return NULL; } last->nelts = 0;//已使用的片的数量为0 last->next = NULL; l->last->next = last;//新创建的chunck为最后一个chunk l->last = last; } elt = (char *) last->elts + l->size * last->nelts;//返回可以使用的内存空间地址(根据当前chunck中已经使用的片数量) last->nelts++;//当前chunck中使用的片数量+1 return elt; }