Nginx源码剖析之内存池和内存管理

  Nginx(发音同 engine x)是一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,并在一个BSD-like 协议下发行。由俄罗斯的程序设计师Igor Sysoev所开发,最初供俄国大型的入口网站及搜寻引擎Rambler(俄文:Рамблер)使用。  

  其特点是占有内存少,并发能力强,事实上nginx的并发能力确实在同类型的网页服务器中表现较好,目前中国大陆使用nginx网站用户有:新浪、网易、 腾讯,另外知名的微网志Plurk也使用nginx,以及诸多暂不曾得知的玩意儿。

  读者可以到此处下载Nginx最新版本的源码:。同时,本文本不想给源码太多注释,因为这不像讲解算法,算法讲解的越通俗易懂越好,而源码剖析则不同,缘由在于不同的读者对同一份源码有着不同的理解,或深或浅,所以,更多的是靠读者自己去思考与领悟。

  ok,本文之中有任何疏漏或不正之处,恳请批评指正。谢谢。

Nginx源码剖析之内存池1、内存池结构

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

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; //内存池的数据块(上面已有描述),设为d
size_t max; //数据块大小,小块内存的最大值
ngx_pool_t *current; //指向当前或本内存池
ngx_chain_t *chain; //该指针挂接一个ngx_chain_t结构
ngx_pool_large_t *large; //指向大块内存分配,nginx中,大块内存分配直接采用标准系统接口malloc
ngx_pool_cleanup_t *cleanup; //析构函数,挂载内存释放时需要清理资源的一些必要操作
ngx_log_t *log; //内存分配相关的日志记录
};

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

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

  其它的数据结构与相关定义:

typedef struct {
ngx_fd_t fd;
u_char *name;
ngx_log_t *log;
} ngx_pool_cleanup_file_t; #define NGX_MAX_ALLOC_FROM_POOL (ngx_pagesize - 1) //在x86体系结构下,该值一般为4096B,即4K

  上述这些数据结构的逻辑结构图如下:

Nginx源码剖析之内存池和内存管理

1.1、ngx_pool_t的逻辑结构

再看一下用UML绘制的ngx_pool_t的逻辑结构图:

Nginx源码剖析之内存池和内存管理

在下一节,我们将会深入分析内存管理的主要函数。

Nginx源码剖析之内存管理2、内存池操作

2.1、创建内存池

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);
//ngx_memalign()函数执行内存分配,该函数的实现在src/os/unix/ngx_alloc.c文件中(假定NGX_HAVE_POSIX_MEMALIGN被定义):

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;
//最大不超过4095B,别忘了上面NGX_MAX_ALLOC_FROM_POOL的定义

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

return p;
}

例如,调用ngx_create_pool(1024, 0x80d1c4c)后,创建的内存池物理结构如下图:

Nginx源码剖析之内存池和内存管理

紧接着,咱们就来分析下上面代码中所提到的:ngx_memalign()函数。

void *
ngx_memalign(size_t alignment, size_t size, ngx_log_t *log)
{
void *p;
int err;

err = posix_memalign(&p, alignment, size);
//该函数分配以alignment为对齐的size字节的内存大小,其中p指向分配的内存块。

if (err) {
ngx_log_error(NGX_LOG_EMERG, log, err,
"posix_memalign(%uz, %uz) failed", alignment, size);
p = NULL;
}

ngx_log_debug3(NGX_LOG_DEBUG_ALLOC, log, 0,
"posix_memalign: %p:%uz @%uz", p, size, alignment);

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

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

2.1、销毁内存池

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/10920.html