`
Poechant
  • 浏览: 229506 次
博客专栏
Bebe66e7-3a30-3fc9-aeea-cfa3b474b591
Nginx高性能Web服务...
浏览量:24321
5738817b-23a1-3a32-86de-632d7da73b1e
Cumulus实时媒体服务...
浏览量:22103
社区版块
存档分类
最新评论

Nginx源码完全注释(7)ngx_palloc.h/ngx_palloc.c

 
阅读更多

Nginx源码完全注释(7)ngx_palloc.h/ngx_palloc.c

  • 作者:钟超
  • 邮箱:zhongchao.ustc#gmail.com (# -> @)
  • 博客:Blog.CSDN.net/Poechant
  • 日期:2012年9月28日

ngx_palloc.h


/*
 * NGX_MAX_ALLOC_FROM_POOL should be (ngx_pagesize - 1), i.e. 4095 on x86.
 * On Windows NT it decreases a number of locked pages in a kernel.
 */
#define NGX_MAX_ALLOC_FROM_POOL  (ngx_pagesize - 1)

#define NGX_DEFAULT_POOL_SIZE    (16 * 1024)

#define NGX_POOL_ALIGNMENT       16
#define NGX_MIN_POOL_SIZE                                                     \
    ngx_align((sizeof(ngx_pool_t) + 2 * sizeof(ngx_pool_large_t)),            \
              NGX_POOL_ALIGNMENT)


typedef void (*ngx_pool_cleanup_pt)(void *data);

typedef struct ngx_pool_cleanup_s  ngx_pool_cleanup_t;

struct ngx_pool_cleanup_s {
    ngx_pool_cleanup_pt   handler;
    void                 *data;
    ngx_pool_cleanup_t   *next;
};


typedef struct ngx_pool_large_s  ngx_pool_large_t;

struct ngx_pool_large_s {
    ngx_pool_large_t     *next;
    void                 *alloc;
};


typedef struct {
    u_char               *last;     // 数据存储的已用区尾地址
    u_char               *end;      // 数据存储区的尾地址
    ngx_pool_t           *next;     // 下一个内存池地址
    ngx_uint_t            failed;   // 失败次数
} ngx_pool_data_t;


struct ngx_pool_s {
    ngx_pool_data_t       d;            // 数据区
    size_t                max;          // 内存池的最大存储空间
    ngx_pool_t           *current;      // 内存池
    ngx_chain_t          *chain;
    ngx_pool_large_t     *large;        // 用于存储大数据,链表结构
    ngx_pool_cleanup_t   *cleanup;      // 用于清理,链表结构
    ngx_log_t            *log;
};


typedef struct {
    ngx_fd_t              fd;           // 文件描述符,用于 ngx_pool_cleanup_file
    u_char               *name;         // 文件名,用于 ngx_pool_delete_file
    ngx_log_t            *log;
} ngx_pool_cleanup_file_t;


void *ngx_alloc(size_t size, ngx_log_t *log);
void *ngx_calloc(size_t size, ngx_log_t *log);

ngx_pool_t *ngx_create_pool(size_t size, ngx_log_t *log);
void ngx_destroy_pool(ngx_pool_t *pool);
void ngx_reset_pool(ngx_pool_t *pool);

void *ngx_palloc(ngx_pool_t *pool, size_t size);
void *ngx_pnalloc(ngx_pool_t *pool, size_t size);
void *ngx_pcalloc(ngx_pool_t *pool, size_t size);
void *ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment);
ngx_int_t ngx_pfree(ngx_pool_t *pool, void *p);


ngx_pool_cleanup_t *ngx_pool_cleanup_add(ngx_pool_t *p, size_t size);
void ngx_pool_run_cleanup_file(ngx_pool_t *p, ngx_fd_t fd);
void ngx_pool_cleanup_file(void *data);
void ngx_pool_delete_file(void *data);

ngx_palloc.c



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

// 创建 size 大小的内存池
ngx_pool_t *
ngx_create_pool(size_t size, ngx_log_t *log)
{
    ngx_pool_t  *p;

    p = ngx_memalign(NGX_POOL_ALIGNMENT, size, log);
    if (p == NULL) {
        return NULL;
    }

    p->d.last = (u_char *) p + sizeof(ngx_pool_t);
    p->d.end = (u_char *) p + size;
    p->d.next = NULL;
    p->d.failed = 0;

    size = size - sizeof(ngx_pool_t);
    p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL;

    p->current = p;
    p->chain = NULL;
    p->large = NULL;
    p->cleanup = NULL;
    p->log = log;

    return p;
}

// 销毁内存池 pool
void
ngx_destroy_pool(ngx_pool_t *pool)
{
    ngx_pool_t          *p, *n;
    ngx_pool_large_t    *l;
    ngx_pool_cleanup_t  *c;

    // 处理 pool->cleanup 链表,处理函数由此前赋值到 pool->cleanup->handler 的函数指针确定
    for (c = pool->cleanup; c; c = c->next) {
        if (c->handler) {
            ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0,
                           "run cleanup: %p", c);
            c->handler(c->data);
        }
    }

    // 释放 pool->large 链表
    for (l = pool->large; l; l = l->next) {

        ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0, "free: %p", l->alloc);

        if (l->alloc) {
            ngx_free(l->alloc);
        }
    }

#if (NGX_DEBUG)

    /*
     * we could allocate the pool->log from this pool
     * so we cannot use this log while free()ing the pool
     */

    for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {
        ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, pool->log, 0,
                       "free: %p, unused: %uz", p, p->d.end - p->d.last);

        if (n == NULL) {
            break;
        }
    }

#endif

    // 释放 pool->d 链表
    for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {
        ngx_free(p);

        if (n == NULL) {
            break;
        }
    }
}

// 重置内存池
void
ngx_reset_pool(ngx_pool_t *pool)
{
    ngx_pool_t        *p;
    ngx_pool_large_t  *l;

    // 释放 large 链的每个节点的内存
    for (l = pool->large; l; l = l->next) {
        if (l->alloc) {
            ngx_free(l->alloc);
        }
    }

    pool->large = NULL;

    // 重置数据 d 链的每个节点,即重置每个节点的可用区首地址 d.last
    for (p = pool; p; p = p->d.next) {
        p->d.last = (u_char *) p + sizeof(ngx_pool_t);
    }
}

// 从内存池 pool 分配大小为 size 的内存块,并返回其地址
// 是被外部使用最多的内存池相关 API,并且考虑对齐问题
void *
ngx_palloc(ngx_pool_t *pool, size_t size)
{
    u_char      *m;
    ngx_pool_t  *p;

    // 如果还未超出内存池的 max 值,超过了则用 large
    if (size <= pool->max) {

        p = pool->current;

        do {
        
            // 对齐内存
            m = ngx_align_ptr(p->d.last, NGX_ALIGNMENT);

            // 该节点剩余可用空间够用
            if ((size_t) (p->d.end - m) >= size) {
                p->d.last = m + size;

                return m;
            }

            // 该节点剩余空间不够用,看下一个节点
            p = p->d.next;

        } while (p);

        // 现有节点都不给力,重新分配一个 d 节点
        return ngx_palloc_block(pool, size);
    }

    // size 超过 pool->max,从 large 取
    return ngx_palloc_large(pool, size);
}

// 类似 ngx_palloc,不考虑对齐问题
void *
ngx_pnalloc(ngx_pool_t *pool, size_t size)
{
    u_char      *m;
    ngx_pool_t  *p;

    if (size <= pool->max) {

        p = pool->current;

        do {
            m = p->d.last;

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

                return m;
            }

            p = p->d.next;

        } while (p);

        return ngx_palloc_block(pool, size);
    }

    return ngx_palloc_large(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, *current;

    // pool 结构定义区和 pool->d 数据区的总大小
    psize = (size_t) (pool->d.end - (u_char *) pool);

    // 分配 psize 大小的内存
    m = ngx_memalign(NGX_POOL_ALIGNMENT, psize, pool->log);
    if (m == NULL) {
        return NULL;
    }

    // 用 new 来表示上面分配的新内存块
    new = (ngx_pool_t *) m;

    // 初始化这个 new,设定 new 的 d.end、d.next、d.failed
    new->d.end = m + psize;
    new->d.next = NULL;
    new->d.failed = 0;

    // m 加上内存池数据定义结构体的大小
    m += sizeof(ngx_pool_data_t);
    // 内存对齐 m
    m = ngx_align_ptr(m, NGX_ALIGNMENT);
    // 设定 new 的 d.last
    new->d.last = m + size;

    current = pool->current;

    // TODO
    for (p = current; p->d.next; p = p->d.next) {
        if (p->d.failed++ > 4) {
            current = p->d.next;
        }
    }

    // new 节点放入内存池数据链
    p->d.next = new;

    pool->current = current ? current : new;

    return m;
}


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

    // 分配 size 大小的内存
    p = ngx_alloc(size, pool->log);
    if (p == NULL) {
        return NULL;
    }

    n = 0;

    // 在 pool 的 large 链中寻找存储区为空的节点,把新分配的内存区首地址赋给它
    for (large = pool->large; large; large = large->next) {
    
        // 找到 large 链末尾,在其后插入之,并返回给外部使用
        if (large->alloc == NULL) {
            large->alloc = p;
            return p;
        }

        // 查看的 large 节点超过 3 个,不再尝试和寻找,由下面代码实现创建新 large 节点的逻辑
        if (n++ > 3) {
            break;
        }
    }

    // 创建 large 链的一个新节点,如果失败则释放刚才创建的 size 大小的内存,并返回 NULL
    large = ngx_palloc(pool, sizeof(ngx_pool_large_t));
    if (large == NULL) {
        ngx_free(p);
        return NULL;
    }

    // 一切顺利,善后工作
    large->alloc = p;
    large->next = pool->large;
    pool->large = large;

    return p;
}


void *
ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment)
{
    void              *p;
    ngx_pool_large_t  *large;

    // 创建一块 size 大小的内存,内存以 alignment 字节对齐
    p = ngx_memalign(alignment, size, pool->log);
    if (p == NULL) {
        return NULL;
    }

    // 创建一个 large 节点
    large = ngx_palloc(pool, sizeof(ngx_pool_large_t));
    if (large == NULL) {
        ngx_free(p);
        return NULL;
    }

    // 将这个新的 large 节点交付给 pool 的 large 字段
    large->alloc = p;
    large->next = pool->large;
    pool->large = large;

    return p;
}


ngx_int_t
ngx_pfree(ngx_pool_t *pool, void *p)
{
    ngx_pool_large_t  *l;

    // 逐一释放 large 链表的每一个节点
    for (l = pool->large; l; l = l->next) {
        if (p == l->alloc) {
            ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0,
                           "free: %p", l->alloc);
            ngx_free(l->alloc);
            l->alloc = NULL;

            return NGX_OK;
        }
    }

    return NGX_DECLINED;
}

// 封装 palloc 为 pcalloc,实现分配内存并初始化为 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;
}

// 向 cleanup 链添加 p->cleanup 这个节点
ngx_pool_cleanup_t *
ngx_pool_cleanup_add(ngx_pool_t *p, size_t size)
{
    ngx_pool_cleanup_t  *c;

    // 创建一个 cleanup 节点
    c = ngx_palloc(p, sizeof(ngx_pool_cleanup_t));
    if (c == NULL) {
        return NULL;
    }

    if (size) {
        // cleanup 节点数据区
        c->data = ngx_palloc(p, size);
        if (c->data == NULL) {
            return NULL;
        }

    } else {
        c->data = NULL;
    }

    // 善后
    c->handler = NULL;
    c->next = p->cleanup;

    p->cleanup = c;

    ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, p->log, 0, "add cleanup: %p", c);

    return c;
}

// 查找指定的 fd,且其 handler 为 ngx_pool_cleanup_file,执行相应动作
// 这里面有一个遍历的操作
void
ngx_pool_run_cleanup_file(ngx_pool_t *p, ngx_fd_t fd)
{
    ngx_pool_cleanup_t       *c;
    ngx_pool_cleanup_file_t  *cf;

    for (c = p->cleanup; c; c = c->next) {
        if (c->handler == ngx_pool_cleanup_file) {

            cf = c->data;

            if (cf->fd == fd) {
                c->handler(cf);
                c->handler = NULL;
                return;
            }
        }
    }
}

// 释放文件描述符
void
ngx_pool_cleanup_file(void *data)
{
    ngx_pool_cleanup_file_t  *c = data;

    ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, c->log, 0, "file cleanup: fd:%d",
                   c->fd);

    if (ngx_close_file(c->fd) == NGX_FILE_ERROR) {
        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
                      ngx_close_file_n " \"%s\" failed", c->name);
    }
}

// 从文件系统删除文件,data 指针指向一个 ngx_pool_cleanup_file_t 类型的数据
void
ngx_pool_delete_file(void *data)
{
    ngx_pool_cleanup_file_t  *c = data;

    ngx_err_t  err;

    ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, c->log, 0, "file cleanup: fd:%d %s",
                   c->fd, c->name);

    // 删除文件
    if (ngx_delete_file(c->name) == NGX_FILE_ERROR) {
        err = ngx_errno;

        if (err != NGX_ENOENT) {
            ngx_log_error(NGX_LOG_CRIT, c->log, err,
                          ngx_delete_file_n " \"%s\" failed", c->name);
        }
    }

    // 关闭对应的文件描述符
    if (ngx_close_file(c->fd) == NGX_FILE_ERROR) {
        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
                      ngx_close_file_n " \"%s\" failed", c->name);
    }
}


#if 0

static void *
ngx_get_cached_block(size_t size)
{
    void                     *p;
    ngx_cached_block_slot_t  *slot;

    if (ngx_cycle->cache == NULL) {
        return NULL;
    }

    slot = &ngx_cycle->cache[(size + ngx_pagesize - 1) / ngx_pagesize];

    slot->tries++;

    if (slot->number) {
        p = slot->block;
        slot->block = slot->block->next;
        slot->number--;
        return p;
    }

    return NULL;
}

-

转载请注明来自柳大·Poechant(钟超)的CSDN博客

-

分享到:
评论

相关推荐

    Nginx源代码分析.pdf

    主要在 os/unix/ngx_alloc.{h,c} 和 core/ngx_palloc.{h,c} 下。其中 os/unix/ngx_alloc.{h,c} 封装了最基本的内存分配函数,是对 C 原有的 malloc/free/memalign 等原有的函数的封装,對應的函数為: * ngx_alloc ...

    Nginx源代码分析

    Nginx对C语言的字符串类型进行了简单的封装,core/ngx_string.h/c里面包含这些封装的内容。包括: * ngx_str_t:字符串类型,包含字符串长度和字符串数据 * ngx_keyval_t:键值对类型,包含键和值的字符串 * ngx_...

    nginx-1.14.2 源码

    Nginx 的配置文件解析由 `src/core/nginx.h` 和 `src/core/ngx_conf_file.c` 等文件实现。它们将配置文件转化为内存中的配置结构,供后续的请求处理使用。 **4. 反向代理与负载均衡** 在 `src/http/modules` 目录...

    cpp-nginx192源码通读分析注释

    本文将聚焦于Nginx 1.9.2版本的源码,通过详尽的函数分析和调用流程注释,帮助读者构建起对Nginx内核机制的深刻理解。 1. **模块化设计** Nginx采用模块化的设计思想,核心部分与功能模块分离,如HTTP模块、事件...

    nginx-0.8.4源代码

    `ngx_string.h`和`ngx_palloc.h`等头文件定义了Nginx内部使用的数据结构和内存管理函数。 6. **conf**:配置文件解析器位于`conf`目录,如`ngx_conf_file.c`,负责读取和解析Nginx的配置文件。 通过阅读和理解...

    自实现nginx-palloc的内存池

    Nginx使用了一种称为“内存池”的特殊内存分配策略,名为`ngx_palloc`,以提高内存分配和释放的效率,减少碎片并优化性能。本文将深入探讨“自实现nginx-palloc的内存池”这一主题,揭示其设计原理、优势以及如何...

    nginx 源码剖析

    **Nginx 源码剖析** Nginx是一款高性能的HTTP和反向代理服务器,以其轻量级、高并发处理能力以及优秀的稳定性而被广泛应用于互联网服务中。深入理解Nginx的源码,有助于我们更好地优化配置,解决实际问题,甚至进行...

    mem_pool.zip

    Nginx提供了一系列的内存池操作函数,如`ngx_palloc()`, `ngx_pcalloc()`, `ngx_pfree()`等,它们分别用于分配内存、分配并清零内存以及释放内存。这些函数隐藏了内存管理的细节,简化了程序员的工作。 五、内存池...

    Nginx源码研究.pdf

    《Nginx源码研究》深入探讨了这款高性能Web服务器的核心技术,对于理解其高效运行机制具有重要价值。本文将围绕内存池、ARRAY、QUEUE和HASH TABLE等关键概念进行详细阐述。 首先,内存池是Nginx实现高效内存管理的...

    Nginx_study:学习Nginx原始码和架构

    Nginx_study编写说明本次学习二进制分为多个阶段第一阶段:完成逐步架构,基本功能,各模块能独立运行阶段一1.1封装...实现内存池在nginx的原始码中内存池的定义和实现在core / ngx_palloc.c和core / ngx_palloc.h中,

    Nginx 内存模型.

    ### Nginx内存模型详解 ...通过对 `ngx_create_pool` 和 `ngx_palloc` 这两个核心函数的理解,我们可以更深入地了解Nginx是如何高效管理内存的。这种设计不仅提高了内存分配的效率,也保证了系统的稳定性和可靠性。

    nginx原码版本0.54

    在 `src/core/ngx_palloc.c` 中,我们可以看到如何创建和使用内存池。 8. **日志系统** Nginx 的日志系统非常灵活,可以在 `src/core/ngx_log.c` 中看到其实现。它支持自定义日志格式、日志级别以及日志写入方式,...

    C++ Nginx内存池源码与使用

    本文将深入探讨C++中Nginx内存池的源码与使用方法。 内存池是一种预先分配一大块连续内存,然后从中按需分配小块内存的策略。这种方式避免了系统级别的内存分配开销,提高了内存分配效率。Nginx内存池设计的核心...

    nginx-1.7.8源码

    5. **内存管理**: 研究 `src/core/ngx_palloc.c`,了解 Nginx 自有的内存池管理机制。 6. **信号处理**: 分析 `src/os/unix/ngx_process_cycle.c`,理解 Nginx 如何处理进程间的通信和信号。 深入研究 Nginx 1.7.8 ...

    nginx内存池代码封装

    `ngx_palloc`和`ngx_pcalloc`类似,但`ngx_palloc`不进行清零操作;`ngx_pnalloc`则是分配不小于请求大小的内存,避免内存碎片。 5. **内存释放** 在Nginx内存池中,我们并不直接释放单个内存块,而是通过`ngx_...

Global site tag (gtag.js) - Google Analytics