Nginx源码分析--数组(转)
原文地址:http://blog.csdn.net/marcky/article/details/5747431
备注:以下关于Nginx源码的分析基于淘宝开源项目Tengine.
Nginx中对数组进行了封装,关于数组的源码定义位于tengine/src/core/ngx_array.c|h中。
Nginx中使用以下结构来维护数组:
16 typedef struct { 17 void *elts; 18 ngx_uint_t nelts; 19 size_t size; 20 ngx_uint_t nalloc; 21 ngx_pool_t *pool; 22 } ngx_array_t; 23
1. elts指向一块向系统申请的内存;在nginx中,这块内存是通过malloc或者其他类似操作从内存池中申请而来的。例如,在tengine/src/core/ngx_array.h中,通过如下语句对elts成员进行赋值:关于ngx_palloc函数的定义可以参考tengine/src/core/ngx_palloc.c.
array->elts = ngx_palloc(pool, n * size);
ngx_palloc的函数声明为:
void * ngx_palloc(ngx_pool_t *pool, size_t size);
2. nelts记录了数组中已存放元素的个数。
3. size是数组元素的大小,可以通过sizeof()求得。
4. nalloc是整个数组可存放元素的单元数。
5. pool当然就是内存池了,用其保存分配此数组的内存池地址。
通过对数组结构成员的了解,可以得之:elts所指的内存大小 = size * nalloc。
nginx给我们提供了5个接口函数,方便进行数组的操作,分别是:
ngx_array_t *ngx_array_create(ngx_pool_t *p, ngx_uint_t n, size_t size);
此函数即是用来创建一个可以存储n个元素,每个元素大小为size的数组。返回值是维护数组的结构的地址。在ngx_array_create函数中同样会调用ngx_palloc函数进行存储空间的分配。
void ngx_array_destroy(ngx_array_t *a);
此函数顾名思义就是释放内存、销毁数组,但nginx并没有真正的释放内存,而是将内存归还给内存池(a->pool)。
void *ngx_array_push(ngx_array_t *a);
此函数的功能就是取得下一个可以存放元素的单元地址。由于nginx封装的数组可以根据需求动态扩展,所以此函数必须处理预分配的内存不足的情况。nginx在ngx_array_push函数内存采用了两种策略来动态增加内存。
- 预分配的内存不足了,但数组所在的内存池槽还有可以分配一个元素的空闲内存,那就在数组末端分配一个元素的空间来暂时满足当前需求。
- 预分配的内存不足,同时数组所在的内存池槽的空闲内存也不足以分配一个元素的空间了,那就向内存池申请一个原数组2倍大小的新内存空间,再将原数组复制到新的位置,最后返回下一个空闲元素位置给我们使用。
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 = a->size * a->nalloc; p = a->pool; 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; }
此接口函数是nginx模块开发中,使用最多的数组操作接口了,列举一个简单的使用例子:
typedef struct { int age; char *name; } person_t; person_t *p = ngx_array_push(a); /*a 是一个被创建的用来存放person_t类型元素的数组*/ //申请内存 p->age = 23; //往内存中填充数据 p->name = (char *)"marcky";
nginx数组的用法就是先申请内存,然后再向内存中填充数据;这是俄罗斯大牛在整个nginx中的编码手法。
void *ngx_array_push_n(ngx_array_t *a, ngx_uint_t n);
从函数名也可以猜到它是用来获得n个元素的内存空间地址,然后从获得的地址向数组中填入n个元素。好像使用得比较少。此操作面临数组空间不足的时候,也是采用了类似ngx_array_push的两个动态增加内存的策略。但存在略微的不同之处。读源码可以了解到。
static ngx_inline ngx_int_t ngx_array_init(ngx_array_t *array, ngx_pool_t *pool, ngx_uint_t n, size_t size)
此接口函数用来为我们初始化一个定义的数组。用法举例:
ngx_array_t a; /*定义数组a,但数组a只是一个维护数组的结构,还没有真正存放元素的内存空间,所以得初始化它。*/ ngx_array_init(&a, p, 10, sizeof(int) ); /*将数组初始化为可以存放10个整型元素的数组*/