您的位置:首页 > 运维架构 > Nginx

nginx源码剖析(二) —— ngx_list_t分析

2015-12-08 19:44 796 查看
前言

       nginx_list_t是nginx的一个链表容器,频繁的在nginx的源码中使用,例如,HTTP的头部就是使用ngx_list_t这个数据结构来存储的所以,今天我们一起来聊聊这个比较重要的数据结构。(这里使用的源码为nginx比较新的一个版本,nginx-1.6.2版本。)

正文

1、nginx_list_t数据结构的描述:



//链表节点结构
typedef struct ngx_list_part_s  ngx_list_part_t;

struct ngx_list_part_s {
void             *elts;             //指向数据数组
ngx_uint_t        nelts;            //实际使用元素的个数
ngx_list_part_t  *next;             //指向下一个节点
};

//头节点结构
typedef struct {
ngx_list_part_t  *last;             //指向链表的尾节点
ngx_list_part_t   part;             //链表头
size_t            size;             //元素的大小
ngx_uint_t        nalloc;           //元素最大个数
ngx_pool_t       *pool;             //指向内存池
} ngx_list_t;
       其中,要注意的是:1、void *是可以指向任意类型的地址。2、头结点结构体中nginx源码使用的不是指针而是结构体变量。

2、对链表操作的描述: 

       nginx源码中,对该链表的操作一共包含三个,函数声明如下所示:

//创建链表
ngx_list_t *ngx_list_create(ngx_pool_t *pool, ngx_uint_t n, size_t size);
//链表头初始化
//注:nginx源码 用ngx_int_t 来替代int
//              用ngx_uint_t来替代unsigned int
static ngx_inline ngx_int_t
ngx_list_init(ngx_list_t *list, ngx_pool_t *pool, ngx_uint_t n, size_t size);
//尾插
void *ngx_list_push(ngx_list_t *list);
      由于我这次将代码的注释写的比较详细,所以就直接将代码贴下。这三个函数的具体实现如下:   
//链表头初始化
//注:nginx源码 用ngx_int_t 来替代int
//              用ngx_uint_t来替代unsigned int
static ngx_inline ngx_int_t
ngx_list_init(ngx_list_t *list, ngx_pool_t *pool, ngx_uint_t n, size_t size)
{
//从内存池中申请数组,用法和malloc类似,
//关于内存池这块,之后的文章咱们再好好聊聊
list->part.elts = ngx_palloc(pool, n * size);
//若空间申请失败则返回错误信息。
if (list->part.elts == NULL) {
return NGX_ERROR;
}
//已使用大小初始化为零
list->part.nelts = 0;
//节点指针域初始化为空
list->part.next = NULL;
//链表控制信息的尾节点使用该链表的
//第一个有效节点的地址进行初始化
list->last = &list->part;
//节点大小使用传入的size_t参数初始化
list->size = size;
//已申请节点大小使用传入的参数n初始化
list->nalloc = n;
//指向内存池的指针使用传入的内存值进行初始化
list->pool = pool;
//初始化完毕,返回初始化成功
return NGX_OK;
}
//链表的创建(主要指对头节点的创建和初始化)
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));
//申请失败则退出,并返回NULL
if (list == NULL) {
return NULL;
}
//初始化头节点,若失败则返回NULL
if (ngx_list_init(list, pool, n, size) != NGX_OK) {
return NULL;
}
//否则,返回创建好的头节点的地址
return list;
}
//尾部添加节点(只是将有效地址返回,不进行实际的元素插入)
void *
ngx_list_push(ngx_list_t *l)
{
void             *elt;
ngx_list_part_t  *last;
//先指向尾节点
last = l->last;

//若该链表的尾节点指向的数组元素已经全部用完
if (last->nelts == l->nalloc) {
//1、申请节点
last = ngx_palloc(l->pool, sizeof(ngx_list_part_t));
//申请失败,返回NULL
if (last == NULL) {
return NULL;
}
//2、申请数组空间,并让该节点的数组指针指向这个数组
last->elts = ngx_palloc(l->pool, l->nalloc * l->size);
//若申请失败则返回NULL
if (last->elts == NULL) {
return NULL;
}
//3、将数组已使用元素个数初始化为0
last->nelts = 0;
//4、将该节点的指针的指向先置为空
last->next = NULL;
//5、将新生成的节点链接到该链表尾部
l->last->next = last;
//6、将头节点的尾指针的指向改为新生成节点
l->last = last;
}
//记录下一个数据在该数组中的位置
elt = (char *) last->elts + l->size * last->nelts;
//已使用的元素个数增加一
last->nelts++;

//返回这个位置
return elt;
}
总结

       通过上面的分析,我们不难发现:

                                  1、nginx源码的ngx_list_t数据结构是从内存池开辟出来的。

                                  2、若数组的空间够,则不申请新的链表节点,使用该剩余空间。

                                  3、若数组空间已满,则在内存池申请新的链表节点。

       至此,我们对nginx的链表容器的源码分析完成。                   
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: