nginx源码:ngx_array_s,ngx_pool_s赏析

注:基于版本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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值