注:基于版本v0.1.0
逻辑上,ngx_array_t依赖ngx_pool_t,所以先来看pool_t.
一 ngx_pool_t
1 ngx_pool_t的结构
可见pool结构是个链表,且是一个单向链表,仅有next指针。每一个链元素,都保留了last和end,来标记可用范围。
struct ngx_pool_s {
char *last; // 上次已使用的内存,即可用内存的开始位置
char *end; // 可用内存的结束位置
ngx_pool_t *next; // 下一个pool
ngx_pool_large_t *large; // 暂时不关注
ngx_log_t *log; // 日志
};
2 初始化一个pool块,返回一个ngx_pool_t类型。
ngx_pool_t *ngx_create_pool(size_t size, ngx_log_t *log)
{
ngx_pool_t *p;
// ngx_alloc主要是调用malloc分配内存
if (!(p = ngx_alloc(size, log))) {
return NULL;
}
// 唯一要注意,这里的last为何不是从0开始?因为pool保留了前面sizeof(ngx_pool_t)大小的内存
// 用于存放结构体本身的数据。
p->last = (char *) p + sizeof(ngx_pool_t);
p->end = (char *) p + size;
p->next = NULL;
p->large = NULL;
p->log = log;
return p;
}
3 初始化。初始化为固定大小的结构块。如果没有空间,则继续延长这个pool链表。
参数size_t一般是某个结构的size.
void *ngx_pcalloc(ngx_pool_t *pool, size_t size)
{
void *p;
p = ngx_palloc(pool, size);
if (p) {
// 初始化为0
ngx_memzero(p, size);
}
return p;
}
ngx_palloc的具体细节,从第一个可用位置(last)往后分配
size <= (size_t) (pool->end - (char *) pool) - sizeof(ngx_pool_t):这个条件的含义是,size < (pool的结尾-pool的开头-pool结构体的大小),即要分配的空间小于空闲空间。
void *ngx_palloc(ngx_pool_t *pool, size_t size)
{
char *m;
ngx_pool_t *p, *n;
ngx_pool_large_t *large, *last;
if (size <= (size_t) NGX_MAX_ALLOC_FROM_POOL
&& size <= (size_t) (pool->end - (char *) pool) - sizeof(ngx_pool_t))
{
// 如果size小于pool的最大值,则遍历pool链表,找一个是否有能塞进去的块。
for (p = pool, n = pool->next; /* void */; p = n, n = n->next) {
m = ngx_align(p->last);
if ((size_t) (p->end - m) >= size) {
p->last = m + size ;
// 找到了能塞进去的位置,则直接返回
return m;
}
if (n == NULL) {
break;
}
}
/* allocate a new pool block */
// 找到链表最后,还是没有合适的空闲块,则新建一个。
// 注意,新建的pool,大小都是一样的,都是p->end 减 p的大小。
if (!(n = ngx_create_pool((size_t) (p->end - (char *) p), p->log))) {
return NULL;
}
// 这里也很经典,把新建的n,放到链表的最后一个位置。
p->next = n;
m = n->last; // m即是可用内存的开始位置.
n->last += size; // 注意这里,先赋值给m,再调整last位置。
return m;
}
/* allocate a large block */
large = NULL;
last = NULL;
if (pool->large) {
for (last = pool->large; /* void */ ; last = last->next) {
if (last->alloc == NULL) {
// 一直找到最后一个large节点,且该节点已分配,但是没有分配内存,则直接使用之
large = last;
last = NULL;
break;
}
if (last->next == NULL) {
// 找到最后一个节点
break;
}
}
}
if (large == NULL) {
if (!(large = ngx_palloc(pool, sizeof(ngx_pool_large_t)))) {
return NULL;
}
large->next = NULL;
}
#if 0
if (!(p = ngx_memalign(ngx_pagesize, size, pool->log))) {
return NULL;
}
#else
if (!(p = ngx_alloc(size, pool->log))) {
return NULL;
}
#endif
if (pool->large == NULL) {
// 如果没有分配任何large
pool->large = large;
} else if (last) {
// 注意这里,是把新申请的large放到链表的尾部。
last->next = large;
}
large->alloc = p;
return p;
}
二: ngx_array_t
有了pool的认识,再看array就比较清楚了。
可以看到,array有个pool指针。
struct ngx_array_s {
void *elts; //数组的头指针
ngx_uint_t nelts; //数组元素个数
size_t size; //每个数组元素的size
ngx_uint_t nalloc; //能存储的最大元素个数
ngx_pool_t *pool; // 数据存储区域(一个链表)的头指针
};
1 ngx_array_t的初始化, 注意分了两步,数组变量和数据区,分开存储。
可以看到, array本身,array的数据区,用的是同一个pool。
从上面palloc代码中可以看到,如果分配的空闲内存不够,则重新分配一个新pool。如果超过最大值,则启用large块。
// 入参是pool指针,数组元素个数,数组单个元素大小
ngx_array_t *ngx_create_array(ngx_pool_t *p, ngx_uint_t n, size_t size)
{
ngx_array_t *a;
// 先从pool分配sizeof(ngx_array_t)大小的内存,赋值给array变量
ngx_test_null(a, ngx_palloc(p, sizeof(ngx_array_t)), NULL);
// 分配数组数据区的内存,并赋值给elts指针
ngx_test_null(a->elts, ngx_palloc(p, n * size), NULL);
// 记录下来a的pool指针,以便于后面写入元素用到
a->pool = p;
// 当前元素个数0,可容纳元素个数为n
a->nelts = 0;
a->nalloc = n;
a->size = size;
return a;
}
2 ngx_array_t的写入操作。
注意这个push_array, 只是获取可安置新元素的位置(指针)。需要后续把这个指针位置赋值。
用来保存内存的入口。当调用push_array写入数组元素时,分两种情况:
a 如果空间还够用,那么直接返回内存地址。
b 如果pool满了,则会新分配2倍容量。
void *ngx_push_array(ngx_array_t *a)
{
void *elt, *new;
ngx_pool_t *p;
/* 数组已申请元素个数 == 数组已分配的元素个数,即数组已满 */
if (a->nelts == a->nalloc) {
p = a->pool;
/* 如果已分配的数据pool中,有空闲区域,那么继续在pool中扩展 */
if ((char *) a->elts + a->size * a->nelts == p->last
&& (unsigned) (p->end - p->last) >= a->size)
{
p->last += a->size;
a->nalloc++;
/* 没有空闲区域,则分配新空间 */
} else {
ngx_test_null(new, ngx_palloc(p, 2 * a->nalloc * a->size), NULL);
ngx_memcpy(new, a->elts, a->nalloc * a->size);
a->elts = new;
a->nalloc *= 2;
}
}
elt = (char *) a->elts + a->size * a->nelts; //获取可安置的内存位置
a->nelts++; // 元素个数++
return elt;
}