nginx自定的list跟我们常见的list是不太一样的,不同点就在于它的节点,它的节点不像我们常见的list的节点,只能存放一个元素,ngx_list_t的节点实际上是一个固定大小的数组。
在初始化的时候,我们需要设定元素需要占用的空间大小,每个节点数组的容量大小。在添加元素到这个list里面的时候,会在最尾部的节点里的数组上添加元素,如果这个节点的数组存满了,再增加新的节点。
我们看一下数据结构定义:
typedef struct ngx_list_part_s ngx_list_part_t;
struct ngx_list_part_s {
void *elts; // 当前节点内存的起始位置(每个节点内存大小是size*nalloc)
ngx_uint_t nelts; // 当前节点中已存放元素的数量
ngx_list_part_t *next; // next节点
};
typedef struct {
ngx_list_part_t *last; // 指向链表中最后一个节点,这个存在的好处是添加元素很方便啊
ngx_list_part_t part; // 链表头节点,注意这里不是指针,而是list的一部分
size_t size; // 单个元素的大小
ngx_uint_t nalloc; // 每一个节点的容量(能存储nalloc个元素)
ngx_pool_t *pool; // list所在的内存池
} ngx_list_t;
创建链表函数:
ngx_list_t *ngx_list_create(ngx_pool_t *pool, ngx_uint_t n, size_t size);
/* 创建很简单,跟array一样,先分配好结构体的内存,再调用init进行初始化并分配节点elts的内存
create时设定nalloc=n, size=size */
static ngx_inline ngx_int_t
ngx_list_init(ngx_list_t *list, ngx_pool_t *pool, ngx_uint_t n, size_t size);
/* 如果ngx_list_t类型的变量不是通过调用函数ngx_list_create创建的(比如某结构体中有个ngx_list),
就必须调用此函数去初始化,否则往这个list里追加元素会引发不可预知的行为,亦或程序会崩溃 */
添加元素函数:
void *
ngx_list_push(ngx_list_t *l)
{
void *elt;
ngx_list_part_t *last;
last = l->last;
// 如果当前节点满员了,申请下一个节点出来,还是先结构体后内存空间
//(有个问题,如果结构出来了内存区拿不到岂不是浪费空间了又,好吧无所谓了,已经出错了这时)
if (last->nelts == l->nalloc) {
last = ngx_palloc(l->pool, sizeof(ngx_list_part_t));
if (last == NULL) {
return NULL;
}
last->elts = ngx_palloc(l->pool, l->nalloc * l->size);
if (last->elts == NULL) {
return NULL;
}
last->nelts = 0;
last->next = NULL;
l->last->next = last; // 重定向last
l->last = last;
}
elt = (char *) last->elts + l->size * last->nelts;
return elt;
}
对了,再加一段代码,这是nginx的源文件中被注释掉的,我感觉是作者留给我们遍历list的示范:
/*
*
* the iteration through the list:
*
* part = &list.part;
* data = part->elts;
*
* for (i = 0 ;; i++) {
*
* if (i >= part->nelts) {
* if (part->next == NULL) {
* break;
* }
*
* part = part->next;
* data = part->elts;
* i = 0;
* }
*
* ... data[i] ...
*
* }
*/