Bootstrap

Ubuntu 下 nginx-1.24.0 源码分析 - ngx_palloc_large 函数

 ngx_palloc_large

声明在 src\core\ngx_palloc.c 中

static void *ngx_palloc_large(ngx_pool_t *pool, size_t size);

定义在 src\core\ngx_palloc.c

static void *
ngx_palloc_large(ngx_pool_t *pool, size_t size)
{
    void              *p;
    ngx_uint_t         n;
    ngx_pool_large_t  *large;

    p = ngx_alloc(size, pool->log);
    if (p == NULL) {
        return NULL;
    }

    n = 0;

    for (large = pool->large; large; large = large->next) {
        if (large->alloc == NULL) {
            large->alloc = p;
            return p;
        }

        if (n++ > 3) {
            break;
        }
    }

    large = ngx_palloc_small(pool, sizeof(ngx_pool_large_t), 1);
    if (large == NULL) {
        ngx_free(p);
        return NULL;
    }

    large->alloc = p;
    large->next = pool->large;
    pool->large = large;

    return p;
}

 

static void *
ngx_palloc_large(ngx_pool_t *pool, size_t size)
{

 

函数签名

  • 返回值 :返回分配的大块内存地址,类型为 void *
  • 参数
    • pool:指向当前内存池的指针。
    • size:需要分配的大块内存大小。
    void              *p;
    ngx_uint_t         n;
    ngx_pool_large_t  *large;

 局部变量声明

  • p:用于存储分配的大块内存地址。
  • n:计数器,用于限制循环次数。
  • large:指向 ngx_pool_large_t 类型的指针,用于遍历和管理大块内存链表。
    p = ngx_alloc(size, pool->log);
    if (p == NULL) {
        return NULL;
    }

 

分配大块内存

  • 调用 ngx_alloc 函数分配指定大小的内存。
    • ngx_alloc 是对系统分配函数(如 malloc)的封装,通常会记录日志以便调试。
    • 如果分配失败(返回 NULL),函数直接返回 NULL,表示分配失败。
    n = 0;
    for (large = pool->large; large; large = large->next) {
        if (large->alloc == NULL) {
            large->alloc = p;
            return p;
        }
        if (n++ > 3) {
            break;
        }
    }

 

遍历大块内存链表

  • 初始化计数器 n 为 0。
  • 遍历 pool->large 链表:
    • 如果找到某个节点的 alloc 字段为空(即该节点未被使用),将新分配的内存地址赋值给 large->alloc,并返回分配的内存地址 p
    • 如果遍历超过 4 次(n > 3),退出循环。

意图

  • 这里的逻辑是为了复用已有的 ngx_pool_large_t 节点,alloc为NULL的节点(已释放的内存块),避免频繁创建新的节点。
  • 限制遍历次数(最多 4 次)是为了防止链表过长时性能下降。

    large = ngx_palloc_small(pool, sizeof(ngx_pool_large_t), 1);
    if (large == NULL) {
        ngx_free(p);
        return NULL;
    }

 

创建新的大块内存节点

  • 调用 ngx_palloc_small 分配一个新的 ngx_pool_large_t 节点。
    • sizeof(ngx_pool_large_t) 是节点的大小。
    • 第三个参数 1 表示分配的内存需要对齐。
  • 如果分配失败:
    • 调用 ngx_free 释放之前分配的大块内存 p,避免内存泄漏
    • 返回 NULL,表示分配失败。

    large->alloc = p;
    large->next = pool->large;
    pool->large = large;

 

将新节点插入链表

  • 将新分配的大块内存地址赋值给 large->alloc
  • 将新节点插入到 pool->large 链表的头部:
    • large->next 指向原来的链表头。
    • 更新 pool->large 指向新节点。

  • 使用头插法可以快速插入新节点,提高效率。
  • 新节点始终位于链表头部,便于后续管理。
    return p;
}

 

返回分配的内存地址

  • 最后返回分配的大块内存地址 p

内存生命周期

    A[分配请求] --> B{size <= pool->max}
    B -->|Yes| C[ngx_palloc_small]
    B -->|No| D[ngx_palloc_large]
    D --> E[ngx_alloc直接分配]
    E --> F[挂载large链表]
    F --> G[显式释放或池销毁]