1、array的结构体
ngx_array_t是nginx中定义的数组结构,用于存储数据,其结构体定义如下:
typedef struct { void *elts; //指向分配给数据存储空间的内存起始地址 ngx_uint_t nelts; //当前数组中存储的元素的个数 size_t size; //每个元素的大小 ngx_uint_t nalloc; //数组的大小,即最多可存储的元素个数 ngx_pool_t *pool; //指向该数组所在的内存池 } ngx_array_t;
由于nginx内部的内存都是通过内存池来分配的,因此一般的结构体中都有一个指向内存池的指针,用于内存的回收释放。在为数组分配空间时,通过要分配的元素个数和大小来确定待分配空间的大小,即size * nalloc;然后通过nelts来记录数组中已存在的元素个数,可用来找到数组中下一个空闲空间的起始地址。
2、数组的创建我们一般为每一类待存放的数据初始化一个数组。
ngx_array_t * ngx_array_create(ngx_pool_t *p, ngx_uint_t n, size_t size) { ngx_array_t *a; a = ngx_palloc(p, sizeof(ngx_array_t)); if (a == NULL) { return NULL; } if (ngx_array_init(a, p, n, size) != NGX_OK) { return NULL; } return a; }首先为ngx_array_t这个结构体分配空间,然后调用ngx_array_init() 分配数据的存储空间,并完成初始化,即设置各字段的值。
static ngx_inline ngx_int_t ngx_array_init(ngx_array_t *array, ngx_pool_t *pool, ngx_uint_t n, size_t size) { array->nelts = 0; //完成array结构的初始化 array->size = size; array->nalloc = n; array->pool = pool; array->elts = ngx_palloc(pool, n * size); //为数据分配的存储空间大小为 n * size if (array->elts == NULL) { return NGX_ERROR; } return NGX_OK; }这样就创建了一个array数组了,其结构如下:
3、元素的插入
nginx中元素的插入不是直接将某个元素赋值的,而是先找到待插入元素在数组中的起始地址并返回,然后再由用户程序进行赋值的,以同时插入n个元素为例:
void * ngx_array_push_n(ngx_array_t *a, ngx_uint_t n) { void *elt, *new; size_t size; ngx_uint_t nalloc; ngx_pool_t *p; size = n * a->size; //size为待分配的内存大小 //首先判断当前数组空间是否足够 if (a->nelts + n > a->nalloc) { //case1:当前数组空间不够,需要重新分配资源 p = a->pool; if ((u_char *) a->elts + a->size * a->nalloc == p->d.last && p->d.last + size <= p->d.end) //如果当前数组在该内存块已用空间的尾部,且该内存块的剩余空间足够存放所需空间时,直接将该内存块尾部的size大小的内存分配给数组 { p->d.last += size; a->nalloc += n; } else { //case2:否则如果数组内存空间不够,且其所在内存块的空间也不够时,就要重新分配内存 /* allocate a new array */ nalloc = 2 * ((n >= a->nalloc) ? n : a->nalloc); //分配2倍所需大小的空间 new = ngx_palloc(p, nalloc * a->size); if (new == NULL) { return NULL; } ngx_memcpy(new, a->elts, a->nelts * a->size); //将原来数组的数据copy到新的内存空间中 a->elts = new; //数组存放数据的空间指向新分配的内存空间 a->nalloc = nalloc; //新数组的大小 } } elt = (u_char *) a->elts + a->size * a->nelts; //case3:通过元素个数找到可用空间的起始地址 a->nelts += n; //内存中已用元素加n return elt; }
内存示意图如上所示,且left > n*size时,即对应case1 ,直接在内存块尾部的剩余空间中为数组分配所需空间的大小。其它数组空间不够的情况都对应case2,即都需要重新分配内存空间。然后即可通过元素个数找到数组空闲空间的起始地址。
4、数组的销毁
数组的销毁只是简单的将分配的内存空间返回给内存池
void ngx_array_destroy(ngx_array_t *a) { ngx_pool_t *p; p = a->pool; 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; } }可以发现,将数组销毁时,只是简单的将数组占用的内存空间归还给了内存池,数组中各个字段的值并没有被置0,因此当我们申请数组时,一定要先调用ngx_array_init()将各字段置0,保证内存空间时干净的。
为了保证不频繁的申请内存,应注意提前规划好要申请的空间大小。