Nginx源代码情景分析(3)——Nginx内存管理-1

来源:互联网 发布:网页编程什么工作 编辑:程序博客网 时间:2024/05/17 07:37
第3章 Nginx内存管理

内存管理是各个WEB服务器都相继实现了的独立功能,作为一个满足高性能的WEB服务器,面对各种请求和应答处理流程,必然涉及到内存以及连接的分配与管理,如果完全采用标准的malloc/free函数接口实现内存管理,频繁的调用必然引起性能的低效。Nginx也不例外,采用了短小精干的方式,实现了其特有的内存管理方式。通过这部分的分析学习,希望我们也能达到融会贯通的目的,不仅能深入理解Nginx的内存管理机制,在实际应用中也能学到其独特的机制,并加以运用。对于开发Nginx模块来说,就更要熟悉其内存管理的相关接口。

内存相关的操作主要在文件 os/unix/ngx_alloc.{h,c} 和core/ngx_palloc.{h,c} 中实现。先分析内存管理的几个主要数据结构:

14 typedef struct ngx_pool_s        ngx_pool_t;

 

48 typedef struct {

 49    u_char               *last;

 50    u_char               *end;

 51    ngx_pool_t           *next;

 52    ngx_uint_t            failed;

 53 } ngx_pool_data_t;

Last:当前内存分配结束位置,即下一段可分配内存的起始位置;

End:内存池的结束位置;

Next:链接到下一个内存池

Failded:记录内存分配不能满足需求的失败次数;

ngx_pool_data_t;结构用来维护内存池的数据块,供用户分配之用。

 54

 55

 56 struct ngx_pool_s {

 57    ngx_pool_data_t       d;

 58    size_t                max;

 59    ngx_pool_t           *current;

 60    ngx_chain_t          *chain;

 61    ngx_pool_large_t     *large;

 62    ngx_pool_cleanup_t   *cleanup;

 63    ngx_log_t            *log;

 64 };

d:数据块

max:数据块大小,小块内存的最大值

current:指向本内存池

chain:这里可以挂一个链结构

large:指向大块内存分配,nginx中,大块内存分配直接采用标准系统接口malloc

cleanup:析构函数,挂载内存释放时需要清理资源的一些必要操作;

log:内存分配相关的日志记录

再看看大块数据分配的结构体:

42 struct ngx_pool_large_s {

 43    ngx_pool_large_t     *next;

 44    void                 *alloc;

 45 };

这个组织比较简单就是一个链表。

下图展示了内存的管理组织结构:

接下来,我们深入分析内存管理的主要函数。

(1)ngx_create_pool(size_t size, ngx_log_t *log)

size:分配内存的大小

log:用于日志记录

下面是该函数的具体实现

15 ngx_pool_t *

 16ngx_create_pool(size_t size, ngx_log_t *log)

 17 {

 18    ngx_pool_t  *p;

 19

 20     p= ngx_memalign(NGX_POOL_ALIGNMENT, size, log);

 21    if (p == NULL) {

 22        return NULL;

 23     }

 24

 25    p->d.last = (u_char *) p + sizeof(ngx_pool_t);

 26    p->d.end = (u_char *) p + size;

 27    p->d.next = NULL;

 28    p->d.failed = 0;

 29

 30    size = size - sizeof(ngx_pool_t);

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

 32

 33    p->current = p;

 34    p->chain = NULL;

 35    p->large = NULL;

 36    p->cleanup = NULL;

 37    p->log = log;

 38

 39    return p;

 40 }

第20行ngx_memalign()函数执行内存分配,该函数的实现在src/os/unix/ngx_alloc.c文件中(假定NGX_HAVE_POSIX_MEMALIGN被定义):

50 void *

 51ngx_memalign(size_t alignment, size_t size, ngx_log_t *log)

 52 {

 53    void  *p;

 54    int    err;

 55

 56    err = posix_memalign(&p, alignment, size);

该函数分配以alignment为对齐的size字节的内存大小,其中p指向分配的内存块。

 57

 58    if (err) {

 59        ngx_log_error(NGX_LOG_EMERG, log, err,

 60                       "posix_memalign(%uz, %uz)failed", alignment, size);

 61        p = NULL;

 62     }

 63

 64    ngx_log_debug3(NGX_LOG_DEBUG_ALLOC, log, 0,

 65                    "posix_memalign:%p:%uz @%uz", p, size, alignment);

 66

 67    return p;

 68 }

从这个函数的实现体,我们可以看到p = ngx_memalign(NGX_POOL_ALIGNMENT, size, log);函数分配以NGX_POOL_ALIGNMENT字节对齐的size字节的内存,在src/core/ngx_palloc.h文件中:

23 #define NGX_POOL_ALIGNMENT       16

因此,nginx的内存池分配,是以16字节为边界对齐的。

函数的25—40行,是按照图4的所示的结构进行组织进行初始化,具体实现读者可以对照图4来加以理解。

 

(2)void ngx_destroy_pool(ngx_pool_t *pool)

内存池的销毁函数,pool指向需要销毁的内存池。

43 void

 44ngx_destroy_pool(ngx_pool_t *pool)

 45 {

 46    ngx_pool_t          *p, *n;

 47    ngx_pool_large_t    *l;

 48    ngx_pool_cleanup_t  *c;

 49

 50    for (c = pool->cleanup; c; c = c->next) {

 51        if (c->handler) {

 52            ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0,

 53                            "run cleanup:%p", c);

 54            c->handler(c->data);

 55        }

 56     }

前面讲到,cleanup指向析构函数,用于执行相关的内存池销毁之前的清理工作,如文件的关闭等,清理函数是一个handler的函数指针挂载。因此,在这部分,对内存池中的析构函数遍历调用。

 57

 58    for (l = pool->large; l; l = l->next) {

 59

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

 61

 62        if (l->alloc) {

 63            ngx_free(l->alloc);

 64        }

 65     }

这一部分用于清理大块内存,ngx_free实际上就是标准的free函数,即大内存块就是通过malloc和free操作进行管理的。

 66

 67#if (NGX_DEBUG)

 68

 69    /*

 70     * we could allocate the pool->log from this pool

 71     * so we cannot use this log while free()ing the pool

 72     */

 73

 74    for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next){

 75        ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, pool->log, 0,

 76                        "free: %p, unused:%uz", p, p->d.end - p->d.last);

 77

 78        if (n == NULL) {

 79            break;

 80        }

 81     }

 82

只有debug模式才会执行这个片段的代码,主要是log记录,用以跟踪函数销毁时日志记录。

 83#endif

84

 85    for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next){

 86        ngx_free(p);

 87

 88        if (n == NULL) {

 89            break;

 90        }

 91     }

 92 }

该片段彻底销毁内存池本身。
0 0
原创粉丝点击