Nginx:数据类型 与 数据结构之 ngx_array
Posted 看,未来
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Nginx:数据类型 与 数据结构之 ngx_array相关的知识,希望对你有一定的参考价值。
基本数据类型封装
typedef intptr_t ngx_int_t;
typedef uintptr_t ngx_uint_t;
数据结构
由于nginx对内存分配比 较“吝啬”(只有保证低内存消耗,才可能实现十万甚至百万级别的同时并发连接数),所以这些Nginx数据结构天生都是尽可能少占用内存。
字符串
typedef struct {
size_t len; //字符串的有效长度
u_char *data; //指向字符串起始地址
} ngx_str_t;
我发现它这个源码里面注释是真的少哈。。。
ngx_str_t的data成员指向的并不是普通的字符串,因为这段字符串未必会以’\\0’作 为结尾,所以使用时必须根据长度len来使用data成员。
那到这里有的人就想说了:那这不是更耗内存了吗?怎么还反而少占用内存?
我也想知道为什么呢。有疑惑先留着,看到后面自然就有答案了。
跟字符串相关的方法都要带上长度,不然容易出现越界。
空间配置器
typedef struct {
u_char *last; //当前内存池分配到此处,即下一次分配从此处开始
u_char *end; //内存池结束位置
ngx_pool_t *next; //内存池里面有很多块内存,这些内存块就是通过该指针连成链表的
ngx_uint_t failed; //内存池分配失败次数
} ngx_pool_data_t;
struct ngx_pool_s {
ngx_pool_data_t d; //内存池的数据块
size_t max; //内存池数据块的最大值
ngx_pool_t *current; //指向当前内存池
ngx_chain_t *chain; //该指针挂接一个ngx_chain_t结构,下篇有,但是这个更重要,先放
ngx_pool_large_t *large; //大块内存链表,即分配空间超过max的内存
ngx_pool_cleanup_t *cleanup; //释放内存池的callback
ngx_log_t *log; //日志信息
};
数组
typedef struct {
void *elts; //指向数组的起始地址
ngx_uint_t nelts; //表示数组中已经使用元素数量
size_t size; //限制每个数组元素占用空间大小,也就是用户要存储的一个数据所 占用的字节数必须小于或等于size。
ngx_uint_t nalloc; //最多可存储数据数
ngx_pool_t *pool; //管理内存分配的内存池对象
} ngx_array_t;
//就字面意思呗,也没啥好看的,就普通的初始化空间。
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;
}
static ngx_inline ngx_int_t
ngx_array_init(ngx_array_t *array, ngx_pool_t *pool, ngx_uint_t n, size_t size)
{
/*
* set "array->nelts" before "array->elts", otherwise MSVC thinks
* that "array->nelts" may be used without having been initialized
*/
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;
}
创建数组后内存池的物理结构图如下:
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_push_n则返回可以在该数组数据区中添加n个元素的起始位置,而添加操作即在获得添加位置之后进行
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; //这是干嘛,有点猛啊,哦,size是局部变量
p = a->pool;
if ((u_char *) a->elts + size == p->d.last
&& p->d.last + a->size <= p->d.end) //还没弹尽粮绝呢?那也不对啊,怎么是小等于,那不就会山穷水尽吗
{ //除非,这个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); //否则,扩展数组数据区为原来的2倍
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; //返回该末尾指针,即下一个元素应该存放的位置
}
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;
if (a->nelts + n > a->nalloc) {
/* the array is full */
p = a->pool;
if ((u_char *) a->elts + a->size * a->nalloc == p->d.last
&& p->d.last + size <= p->d.end)
{
/*
* the array allocation is the last in the pool
* and there is space for new allocation
*/
p->d.last += size;
a->nalloc += n;
} else {
/* allocate a new array */
nalloc = 2 * ((n >= a->nalloc) ? n : a->nalloc);
new = ngx_palloc(p, nalloc * a->size);
if (new == NULL) {
return NULL;
}
ngx_memcpy(new, a->elts, a->nelts * a->size);
a->elts = new;
a->nalloc = nalloc;
}
}
elt = (u_char *) a->elts + a->size * a->nelts;
a->nelts += n;
return elt;
}
向数组中添加元素实际上也是在修该内存池的last指针(若数组数据区满)及数组头信息,即使数组满了,需要扩展数据区内容,也只需要内存拷贝完成,并不需要数据的移动操作,这个效率也是相当高的。
太多了慢慢来,一次太多 hold 不住。
以上是关于Nginx:数据类型 与 数据结构之 ngx_array的主要内容,如果未能解决你的问题,请参考以下文章