Chinaunix首页 | 论坛 | 博客
  • 博客访问: 128753
  • 博文数量: 44
  • 博客积分: 86
  • 博客等级: 民兵
  • 技术积分: 249
  • 用 户 组: 普通用户
  • 注册时间: 2011-09-23 22:50
文章分类
文章存档

2012年(32)

2011年(12)

分类:

2011-12-05 16:39:14

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

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

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

说明:

 SEQ \* ARABIC 4 内存管理结构

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

1ngx_create_pool(size_t size, ngx_log_t *log)

size:分配内存的大小

log:用于日志记录

下面是该函数的具体实现

15 ngx_pool_t *

 16 ngx_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 }

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

50 void *

 51 ngx_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字节为边界对齐的。

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

 

2void ngx_destroy_pool(ngx_pool_t *pool)

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

43 void

 44 ngx_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函数,即大内存块就是通过mallocfree操作进行管理的。

 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 }

该片段彻底销毁内存池本身。

阅读(855) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~