1、数据结构
struct ngx_array_s {
void *elts; //数组数据区起始位置
ngx_uint_t nelts; //实际已存放的元素个数
size_t size; //每个元素大小,可用sizeof获得大小
ngx_uint_t nalloc; //数组所含空间个数,即实际分配的小空间的个数
ngx_pool_t *pool; //该数组在此内存池中分配
};
nginx封装了非常好用的数组ngx_array_t,在做nginx模块开发的时候,就不必再使用原始的数组了,这个数组是可以动态扩展的,也可以称为“动态数组”。每个数据项的具体含义是:
(1)elts:指向一块向系统申请的内存,在nginx中,这块内存通过palloc或其它操作从内存池申请而来;
(2)nelts:记录数组中已存放元素的个数;
(3)size:数组元素的大小,可以通过sizeof()求的;
(4)nalloc:整个数组可存放元素的单元数;
(5)pool:内存池,用其保存分配此数组的内存池地址;
备注:sizeof(ngx_array_t)=20,则表明nginx的数组也要从内存池中分配,将分配nalloc个大小为size的小空间,实际分配空间为elts=nalloc*size。
2、数组操作
ngx_array_create(ngx_pool_t *p, ngx_uint_t n, size_t size);
函数功能:用来创建一个可以存储n个元素,每个元素大小为size的数组,返回值是维护数组的结构的地址。
ngx_array_t *< xmlnamespace prefix ="o" ns ="urn:schemas-microsoft-com:office:office" />
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;
}
a->elts = ngx_palloc(p, n * size); //分配n*size大小的区域作为数组数据区
if (a->elts == NULL) {
return NULL;
}
a->nelts = 0; //初始化为0,表示已存放元素
a->size = size;
a->nalloc = n;
a->pool = p;
return a;
}
ngx_array_destroy(ngx_array_t *a);
函数功能:销毁数组主要包括销毁数组数据区和数组头,这里的销毁动作实际上就是修改内存池的last指针,并没有真正调用free等释放内存的操作,而是将内存归还给内存池。
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;
}
}
ngx_array_push(ngx_array_t *a);
函数功能:此函数的功能就是取得下一个可以存放元素的单元地址。此接口函数是nginx模块开发中,使用最多的数组操作接口了。由于nginx封装的数组可以根据需求动态扩展,所以此函数必须处理预分配的内存不足的情况。nginx在ngx_array_push函数内存采用了两种策略来动态增加内存。
(1)预分配的内存不足了,但数组所在的内存池槽还有可以分配一个元素的空闲内存,那就在数组末端分配一个元素的空间来暂时满足当前需求。
(2)预分配的内存不足,同时数组所在的内存池槽的空闲内存也不足以分配一个元素的空间了,那就向内存池申请一个原数组2倍大小的新内存空间,再将原数组复制到新的位置,最后返回下一个空闲元素位置给我们使用。
void *
ngx_array_push(ngx_array_t *a)
{
void *elt, *new;
size_t size;
ngx_pool_t *p;
if (a->nelts == a->nalloc) { //数组数据区已满
size = a->size * a->nalloc; //计算数组数据区的大小
p = a->pool;
if ((u_char *) a->elts + size == p->d.last //若内存池的last指针指向数组数据区的末尾
&& p->d.last + a->size <= p->d.end) //且内存池未使用的区域可以再分配一个size大小的小空间
{
p->d.last += a->size;
a->nalloc++; //实际分配小空间的个数+1
} else {
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; //返回该末尾指针,即下个元素应该存放的位置
}
函数常用方法:
typedef struct {
int age;
char *name;
} person_t;
person_t *p = ngx_array_push(a); //先分配内存
p->age = 23; //初始化
p->name = (char *)"marcky";
备注:向数组中添加元素实际上是修改内存池的last指针及数组头信息,即使数组已满,需要扩展数据区内容,也只需要内存拷贝完成,并不需要数据的移动操作,内存拷贝的效率是相当高的。
ngx_array_push_n(ngx_array_t *a, ngx_uint_t n);
函数功能:用来获得n个元素的内存空间地址,然后从获得的地址向数组中填入n个元素。
ngx_array_init(ngx_array_t *array, ngx_pool_t *pool, ngx_uint_t n, size_t size)
函数功能:初始化数组,ngx_array_init和ngx_array_create的主要区别是ngx_array_init只初始化数组数据区,而ngx_array_create不仅要初始化数组数据区,还要初始化数组头部信息。
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->size = size;
array->nalloc = n;
array->pool = pool;
array->elts = ngx_palloc(pool, n * size);
if (array->elts == NULL) {
return NGX_ERROR;
}
return NGX_OK;
}
3、使用实例
typedef struct {
int age;
char name[256];
} person_t;
ngx_array_t *datas;
person_t *node;
datas = ngx_array_create(pool, 10, sizeof(person_t)); //创建数组
if (datas == NULL){
return NGX_ERROR;
}
node = (person_t *)ngx_array_push(datas); //取得下一个可以存放元素的单元地址
node->age = 16; //填充数据
strcpy(node->name, ”chenjie”, strlen(“chenjie”));
node->name[strlen(“chenjie”)]=0;