Nginx基础. Nginx数组与链表

本文介绍了Nginx中的ngx_array_t和ngx_list_t两种数据结构。ngx_array_t是Nginx提供的数组容器,而ngx_list_t则是一个封装的链表容器,广泛应用于Nginx内部。文章通过对比libevent的TAILQ队列,帮助读者更好地理解Nginx中的链表概念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ngx_array_t 

实现文件: ./src/core/ngx_array.h     ./src/core/ngx_array.c


1. 数据结构定义
typedef struct {
    void        *elts;          //数组数据区起始位置
    ngx_uint_t   nelts;         //当前存放元素个数
    size_t       size;          //存放的每个元素的大小
    ngx_uint_t   nalloc;        //数组最大能存放的元素个数
    ngx_pool_t  *pool;          //使用的内存池对象
} ngx_array_t;
单纯的从结构定义可以看出, 数组是与内存池绑定的, 占用的空间由内存池分配


2. 数组操作
1.)创建
ngx_array_t *ngx_array_create(ngx_pool_t *p, ngx_uint_t n, size_t size);
2.) 初始化
static ngx_inline ngx_int_t
ngx_array_init(ngx_array_t *array, ngx_pool_t *pool, ngx_uint_t n, size_t size);
3.) 销毁
void ngx_array_destroy(ngx_array_t *a);
4.) 加入元素
void *ngx_array_push(ngx_array_t *a);
5.) 加入n个相同元素
void *ngx_array_push_n(ngx_array_t *a, ngx_uint_t n);


3. 操作详解
关于数组的操作, 大多简单. 只拿出几个谈谈
先看的当然是数组的创建函数, 但是它就是简单的内存分配以及变量初始化, 这里就不贴出来了

下面是数组的销毁动作, 主要就是修改了内存池的last指针, 并没有调用free等释放内存的操作,显然,这种维护效率是很高的。
void
ngx_array_destroy(ngx_array_t *a)
{
    ngx_pool_t  *p;

    p = a->pool;

     //下面是对数组内容和其结构的"删除", 实际上只是指针的移动. 如果正巧他们不是正好在last指针前面, 那么也就不管了, 反正内存是统一管理的
    if ((u_char *) a->elts + a->size * a->nalloc == p->d.last) {
        p->d.last -= a->size * a->nalloc;
    }

    if ((u_char *) a + sizeof(ngx_array_t) == p->d.last) {
        p->d.last = (u_char *) a;
    }
}


最后, 看看如何添加一个数组元素
根据之前学习STL的经验, 如果当前元素超过了最大元素个数, 那么就会重新分配一段内存给这个数组
void *
ngx_array_push(ngx_array_t *a)
{
    void        *elt, *new;
    size_t       size;
    ngx_pool_t  *p;

     //如果这段数组内存还可以存的下新的元素, 那么处理就简单了
    if (a->nelts == a->nalloc) {

        /* the array is full */
          //首先size得到原来数组的大小
        size = a->size * a->nalloc;

        p = a->pool;

          //判断last指针是否正好在这段数组内存的后面, 如果正好, 那么我们继续判断是否接下来还有空间可以存数组元素
          //这里就跟STL里的vector不同了. 因为我们是在内存池里操作, 如果发现这段数组内存后面正好还有空余的, 那么就直接利用把, 省的再繁杂的操作
        if ((u_char *) a->elts + size == p->d.last
            && p->d.last + a->size <= p->d.end)
        {
            /*
             * the array allocation is the last in the pool
             * and there is space for new allocation
             */

            p->d.last += a->size;
               //因为这里是在原有数组已满的基础上添加元素,又正好有这段内存, 所以需要修改最大元素存储的数量
            a->nalloc++;

        } else {
            /* allocate a new array */
               //否则的话, 只能老老实实的分配一段新的内存, 且大小是原来的两倍
            new = ngx_palloc(p, 2 * size);
            if (new == NULL) {
                return NULL;
            }
                //将原来的元素都复制过来
            ngx_memcpy(new, a->elts, size);
            a->elts = new;
            a->nalloc *= 2;
        }
    }
     //将新元素需要的内存返回
    elt = (u_char *) a->elts + a->size * a->nelts;
    a->nelts++;

    return elt;
}






ngx_list_t

链表是Nginx封装的链表容器, 它在Nginx中使用的非常频繁.
相比较libevent中的TAILQ队列, Nginx中的list理解起来要容易的多
下面就简单的认识一下这个链表.

1. 数据结构的定义

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;           //链表头中包含的第一个节点(part)  
    size_t            size;           //虽然存储的类型是任意的, 但还是要通过size来限制每个元素占用的空间大小, 即数组中元素大小必须小于或等于size
    ngx_uint_t        nalloc;         //链表的数组元素一旦分配后是不可更改的. nalloc表示每个数组容量
    ngx_pool_t       *pool;
} ngx_list_t;
ngx_list_t描述整个链表, 而ngx_list_part_t只描述链表的一个元素.
要注意的是, ngx_list_t不是一个单纯的链表, 它是存储数组的链表, 即每个链表元素ngx_list_part_t是一个数组, 拥有连续的内存
该数组既依赖ngx_list_t结构中的size和nalloc来表示数组的容量, 同时又依靠每个ngx_list_part_t成员中的nelts来表示当前已经使用了多少容量.

对于这样的设计, 有什么好处呢?
1. 链表中存储的元素是灵活的, 可以是任何一种数据结构
2. 链表元素需要占用的内存由ngx_list_t管理, 它已经通过数组分配好了
3. 小块的内存使用链表访问是低效率的, 使用数组偏移来访问则高效的多


2. 链表的操作
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_init中, 不仅初始化了一些变量, 还分配了 n*size 大小内存的一个数组. 说明最初创建的list会自带一个数组元素

除了初始化, 还有一个要看的就是插入元素了
既然链表的每个元素是数组, 那么新值插入的位置就是最后一个数组元素的末尾, 或是另开一个链表元素来装这个值
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 */

          //继续分配
        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;
        l->last = last;
    }
     //返回新值要插入的空间起始地址
    elt = (char *) last->elts + l->size * last->nelts;
    last->nelts++;

    return elt;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值