当前位置: 首页 > article >正文

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

ngx_palloc_block

声明在 src\core\ngx_palloc.c

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

定义在 src\core\ngx_palloc.c

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);

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

    new = (ngx_pool_t *) m;

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

    m += sizeof(ngx_pool_data_t);
    m = ngx_align_ptr(m, NGX_ALIGNMENT);
    new->d.last = m + size;

    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;

    return m;
}

在深入分析 ngx_palloc_block 函数之前,我们需要了解 Nginx 内存池的基本结构:

  • ngx_pool_t :这是 Nginx 内存池的核心数据结构,表示一个内存池块。

    • 每个 ngx_pool_t 包含一个固定大小的内存区域(称为“当前块”),以及指向下一个内存块的指针。
    • 当前块用完后,会分配新的内存块并链接到链表中。
  • 内存分配策略

    • 小块内存直接从当前块分配。
    • 如果当前块不足以满足需求,则调用 ngx_palloc_block 分配新的内存块

 

ngx_palloc_block 的主要作用是为内存池分配一个新的内存块,以满足当前块无法容纳的内存请求。它的输入参数包括:

  • pool:当前内存池的指针。
  • size:需要分配的内存大小。

函数返回值是一个指向新分配内存的指针。

 

局部变量

u_char      *m;
size_t       psize;
ngx_pool_t  *p, *new;
  • m:指向新分配的内存块的起始地址。
  • psize:当前内存池块的大小。
  • pnew:分别用于遍历内存池链表和指向新分配的内存池块。

计算当前内存池块的大小

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

 

  • pool->d.end 是当前内存池块的结束地址。
  • (u_char *) pool 是当前内存池块的起始地址。
  • psize 表示当前内存池块的总大小。

通过计算当前块的大小,确保新分配的块与现有块大小一致,从而保持内存分配的一致性。

分配新内存块

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

 

  • ngx_memalign 是一个对齐内存分配函数,确保分配的内存地址对齐到指定边界(NGX_POOL_ALIGNMENT)。
  • 如果内存分配失败,返回 NULL

分配一块新的内存区域,大小与当前块相同,并确保内存对齐,避免因未对齐导致的性能问题。

初始化新内存池块

new = (ngx_pool_t *) m;
new->d.end = m + psize;
new->d.next = NULL;
new->d.failed = 0;

 

  • 将新分配的内存块转换为 ngx_pool_t 类型。
  • 设置新块的结束地址为 m + psize
  • 初始化 next 指针为 NULL,表示该块是链表的最后一个节点。
  • 初始化 failed 计数器为 0,用于记录分配失败的次数。

将新分配的内存初始化为一个有效的内存池块,以便后续使用。

调整指针位置

m += sizeof(ngx_pool_data_t);
m = ngx_align_ptr(m, NGX_ALIGNMENT);
new->d.last = m + size;

 

  • m += sizeof(ngx_pool_data_t):跳过 ngx_pool_data_t 结构体的头部信息。
  • m = ngx_align_ptr(m, NGX_ALIGNMENT):对齐指针,确保分配的内存地址符合对齐要求。
  • new->d.last = m + size:设置新块的 last 指针,指向已分配内存的末尾。

调整指针位置,确保新块的内存区域可以被正确使用。

遍历链表并更新 current 指针

for (p = pool->current; p->d.next; p = p->d.next) {
    if (p->d.failed++ > 4) {
        pool->current = p->d.next;
    }
}
  • pool->current 开始遍历链表,直到找到最后一个块。
  • 如果某个块的 failed 计数器超过 4,则更新 pool->current 指针,跳过频繁失败的块。

优化内存分配效率,避免频繁访问分配失败的块。

链接新块到链表

p->d.next = new;

 

  • 将新块链接到链表的末尾。

扩展内存池链表,使其能够容纳更多的内存分配请求。

返回分配的内存地址

return m;

 

  • 返回新分配的内存地址。

完成内存分配操作,供调用者使用。


http://www.kler.cn/a/552608.html

相关文章:

  • golang面试题:两个interface{} 能不能比较?
  • 接口自动化框架篇:Pytest中的接口请求封装!
  • idea日常报错之UTF-8不可映射的字符
  • 游戏引擎学习第108天
  • MySQL数据库(3)—— 表操作
  • 深入解析LVS命令参数及DR模式下的ARP抑制原理
  • MybaitsPlus学习笔记(三)常用注解
  • 【科研绘图系列】R语言绘制时间序列图(time series plot)
  • 【Linux】Socket编程—TCP
  • 数据驱动的自动化本体构建过程包含3个阶 段
  • 编译安装php
  • 深拷贝和浅拷贝的区别
  • 笔试题笔记#7 根据int类型标记判断的BFS和区间覆盖复习
  • 【go语言规范】关于setter和 getter
  • 【Mysql索引在什么情况下会失效?】
  • Diff3Dformer:利用切片序列扩散通过 Transformer 网络增强 3D CT 分类
  • Spring 动态代理 JDK代理和GGLIB代理
  • 前端编程基础开发规范
  • 【练习】【滑动窗口】力扣热题100 438. 找到字符串中所有字母异位词
  • 从插入排序到希尔排序