zl程序教程

您现在的位置是:首页 >  硬件

当前栏目

Nginx内存池:内存分配方案 | 小块内存分配方案

内存Nginx 方案 分配
2023-09-27 14:29:25 时间

内存分配函数

内存分配时考虑了小块和大块,用不同函数实现,
如果申请的空间大小 <= 当前内存池的max,调用ngx_palloc_small();
否则调用 ngx_palloc_large()

1. 考虑内存对齐版本


void *ngx_palloc(ngx_pool_t *pool, size_t size)
{
#if !(NGX_DEBUG_PALLOC)
    if (size <= pool->max) {
        return ngx_palloc_small(pool, size, 1);
    }
#endif

    return ngx_palloc_large(pool, size);
}

2. 不考虑内存对齐版本

void *ngx_pnalloc(ngx_pool_t *pool, size_t size)
{
#if !(NGX_DEBUG_PALLOC)
    if (size <= pool->max) {
        return ngx_palloc_small(pool, size, 0);
    }
#endif

    return ngx_palloc_large(pool, size);
}

3. 初始化为0的版本

在ngx_pcalloc()函数中,依然是调用 ngx_palloc(),但会将申请的空间全部置为0.

void *ngx_pcalloc(ngx_pool_t *pool, size_t size)
{
    void *p;

    p = ngx_palloc(pool, size);
    if (p) {
        ngx_memzero(p, size);
    }

    return p;
}

分配小块内存源码解读

static ngx_inline void *
ngx_palloc_small(ngx_pool_t *pool, size_t size, ngx_uint_t align)
{
    u_char      *m;
    ngx_pool_t  *p;

    p = pool->current;  // 1

    do {
        m = p->d.last;  // 2

        if (align) {	// 3
            m = ngx_align_ptr(m, NGX_ALIGNMENT);
        }

        if ((size_t) (p->d.end - m) >= size) { // 4
            p->d.last = m + size;

            return m;
        }

        p = p->d.next;  // 5

    } while (p);

    return ngx_palloc_block(pool, size);
}

static void *
ngx_palloc_block(ngx_pool_t *pool, size_t size)
{
    u_char      *m;
    size_t       psize;
    ngx_pool_t  *p, *new;

    psize = (size_t) (pool->d.end - (u_char *) pool); // 6

    m = ngx_memalign(NGX_POOL_ALIGNMENT, psize, pool->log);  // 7
    if (m == NULL) {
        return NULL;
    }

    new = (ngx_pool_t *) m; // 8

    new->d.end = m + psize;  // 9
    new->d.next = NULL;
    new->d.failed = 0;

    m += sizeof(ngx_pool_data_t);  // 10
    m = ngx_align_ptr(m, NGX_ALIGNMENT);  // 11
    new->d.last = m + size;  // 12
	
	// 13
    for (p = pool->current; p->d.next; p = p->d.next) {
        if (p->d.failed++ > 4) {
            pool->current = p->d.next;
        }
    }

    p->d.next = new;	// 14

    return m;
}
  1. 进入函数后先申请两个指针,一个是无符号char类型 m,一个是内存池类型的 p,先将p指向当前内存池pool的current,也就是内存池的开头:如图中p。

在这里插入图片描述

  1. 进入do…while()循环,意味着不管循环条件是否满足,都要执行一遍do的语句,第2步让指针 m 指向 p的last,也就是内存池可分配内存的起始位置。

在这里插入图片描述

  1. 判断参数align ,如果为真,需要进行内存对齐处理,否则不进行。
  2. 判断当前内存池可用空间是否大于等于 需要申请的空间size,如果满足条件,那么直接将last指针偏移 size个字节,返回 指针 m 即可。

在这里插入图片描述在这里插入图片描述

  1. 如果上面的步骤没有返回,就让p指向下一个小块的内存池,但初始化时候next域设置成了NULL,所以循环退出了,需要执行下面的ngx_palloc_block()函数。
  2. 进入该函数,先计算内存池pool的总大小,赋值给 psize;
  3. 再次申请一个内存池,大小是 psize,并由 m指向该内存池起始位置。
  4. 让new指针指向m的位置。

在这里插入图片描述

  1. 将这一块内存池的数据域初始化(end指向末尾,next置为NULL,failed置为0)。
  2. m指向该内存池可供使用的起始位置(偏移一个ngx_pool_data_t的字节个数)。

在这里插入图片描述

  1. 将m调节至指定的倍数:宏定义如下
#define ngx_align_ptr(p, a)                                                   \
    (u_char *) (((uintptr_t) (p) + ((uintptr_t) a - 1)) & ~((uintptr_t) a - 1))
  1. 将last指针调节至 m + size的位置,因为这块内存就是要分配出去的。

在这里插入图片描述

  1. 该for()循环是从第一个内存池开始检查,检测 failed标记加一后是否大于4,如果大于4,直接让current指针指向下一个内存池的起始位置,意味着以后都不在这个failed > 4 的内存池申请空间了,因为只剩一点点空间或者不剩空间了。
  2. 让指针 p 的next域指向 new,即和下一块内存池连接起来。
  3. return m,由于last指针指向了 m + size的位置,那意味着返回的m 到 m + size这块空间就是客户需要的那一块内存。