nginx_图解nginx关键数据结构_链表ngx_list_t

nginx_图解nginx关键数据结构之ngx_list_t

1、ngx_list_t的定义

  ngx_list_t是nginx中封装的链表,与普通链表不同的是,每个链表元素中又挂载了一个连续内存空间,也可以理解为挂载了一个数组。ngx_list_t定义在nginx/src/core/ngx_list.h文件中,下面来看看代码:

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_list_part_t中的单个数组元素所占空间大小
    ngx_uint_t        nalloc;//ngx_list_part_t中数组的容量capacity
    ngx_pool_t       *pool;//链表中管理分配的内存池对象
} ngx_list_t;

下图清楚的展示了ngx_list_t的数据结构(假设内存池分配的是连续空间):
在这里插入图片描述
2、ngx_list_t提供的接口

ngx_list_t的接口定义在nginx/src/core/ngx_list.c文件中,下面来看看代码(下面代码中设计到的内存池相关函数在我的另一篇博客中有讲解到,戳这里 》》》nginx_图解nginx关键数据结构内存池 ngx_pool_t

2.1、 链表创建及初始化

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;
    }

    if (ngx_list_init(list, pool, n, size) != NGX_OK) {
        return NULL;
    }

    return list;
}
//初始化函数在ngx_list.h中
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);
    if (list->part.elts == NULL) {
        return NGX_ERROR;
    }

    list->part.nelts = 0;//初始化时已使用0个元素
    list->part.next = NULL;//初始化时链表就一个元素
    list->last = &list->part;//初始化时尾部元素就是第一个元素
    list->size = size;
    list->nalloc = n;
    list->pool = pool;

    return NGX_OK;
}

2.2、向链表中插入新的数组元素

//返回的是新的数组元素应该插入的位置
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 a new list part */
		//分配ngx_list_part_t结构空间
        last = ngx_palloc(l->pool, sizeof(ngx_list_part_t));
        if (last == NULL) {
            return NULL;
        }
		//分配nalloc * size字节大小的空间
        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;
        l->last = last;
    }
	//如果last part没有满,就返回l->size * last->nelts之后的空间地址
	//如果满了,那么此时last->nelts == 0,就返回新的空间的开头
    elt = (char *) last->elts + l->size * last->nelts;
    last->nelts++;
	
    return elt;//成功返回新分配的元素首地址
}

不多bb,直接看图理解上述过程:

(1)last part 未满的情况
在这里插入图片描述
(2)last part 已满的情况
在这里插入图片描述
应用举例:

ngx_list_t* test_list = ngx_list_create(r->pool, 4, sizeof(ngx_str_t));
if(test_list == NULL){
	return NGX_ERROR;
}

ngx_str_t* str = ngx_list_push(test_list);
if(str == NULL){
	return NGX_ERROR;
}
str->len = sizeof("hello world");
str->data = "hello world";

2.3、 加餐!遍历接口以及Tengine额外添加的接口

2.3.1、 遍历接口

 //the iteration through the list:
   part = &list.part;
   data = part->elts;
 //这里i代表数组中元素的索引
   for (i = 0 ;; i++) {
 	//如果i大于等于nelts,说明当前数组已经遍历完
      if (i >= part->nelts) {
      //如果part->next == NULL,说明链表已经遍历完,退出循环
          if (part->next == NULL) {
               break;
          }
 		
          part = part->next;
          data = part->elts;
          i = 0;
      }
      //... opearte data[i] ...
 
 }

2.3.2、 Tengine额外添加的删除元素接口

#if (T_NGX_IMPROVED_LIST)
//删除数组中某个元素的接口
//需要指定链表ngx_list_t、链表元素ngx_list_part_t、索引ngx_uint_t
static ngx_int_t
ngx_list_delete_elt(ngx_list_t *list, ngx_list_part_t *cur, ngx_uint_t i)
{
    u_char *s, *d, *last;

    s = (u_char *) cur->elts + i * list->size;
    d = s + list->size;
    last = (u_char *) cur->elts + cur->nelts * list->size;

    while (d < last) {
        *s++ = *d++;
    }

    cur->nelts--;

    return NGX_OK;
}

//删除ngx_list_t中某个指定数组元素,需要遍历每个part的数组元素
//并调用删除数组元素的接口
ngx_int_t
ngx_list_delete(ngx_list_t *list, void *elt)
{
    u_char          *data;
    ngx_uint_t       i;
    ngx_list_part_t *part, *pre;

    part = &list->part;
    pre = part;
    data = part->elts;

    for (i = 0; /* void */; i++) {

        if (i >= part->nelts) {
            if (part->next == NULL) {
                break;
            }

            i = 0;
            pre = part;
            part = part->next;
            data = part->elts;
        }
		//如果当前part的数组元素i是要删除的元素
        if ((data + i * list->size)  == (u_char *) elt) {
        	//如果list的第一个part不等于当前part 且 当前part占用元素为1,直接删除该part
        	//ngx_list_t至少要有一个part
            if (&list->part != part && part->nelts == 1) {
                pre->next = part->next;
                if (part == list->last) {
                    list->last = pre;
                }

                return NGX_OK;
            }
			//否则就直接删除该元素
            return ngx_list_delete_elt(list, part, i);
        }
    }

    return NGX_ERROR;
}
#endif

同样,图解删除操作:

在这里插入图片描述
在这里插入图片描述
3、Reference

3.1 深入理解nginx(第二版) 陶辉 机械工业出版社
3.2 Tengine社区:https://tengine.taobao.org/

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值