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[显式释放或池销毁]