您的位置:首页 > 理论基础 > 数据结构算法

Nginx高效数据结构(5)——内存池(ngx_pool_t)

2016-11-19 20:41 656 查看
Nginx是我们学习编程的一个非常有参考价值的开源项目。良好的编码风格,高效的数据结构、架构设计。

快课网在此搜罗了一些优质资源。从本文开始讲述Nginx中常用的数据结构,主要包括Nginx的数组结构链表结构队列hash结构内存池等。

0. 序

nginx对内存的管理由其自己实现的内存池结构ngx_pool_t来完成,本文重点叙述nginx的内存管理。

nginx内存管理相关文件:

(1) ./src/os/unix/ngx_alloc.h/.c
内存相关的操作,封装了最基本的内存分配函数
如free/malloc/memalign/posix_memalign,分别被封装为ngx_free,ngx_alloc/ngx_calloc,
ngx_memalign
ngx_alloc:封装malloc分配内存
ngx_calloc:封装malloc分配内存,并初始化空间内容为0
ngx_memalign:返回基于一个指定alignment的大小为size的内存空间,且其地址为alignment的整数倍,alignment为2的幂。

(2) ./src/core/ngx_palloc.h/.c
封装创建/销毁内存池,从内存池分配空间等函数
.表示nginx-1.0.4代码目录,本文为/usr/src/nginx-1.0.4。

 1. 内存池结构

nginx对内存的管理均统一完成,例如,在特定的生命周期统一建立内存池(如main函数系统启动初期即分配1024B大小的内存池),需要内存时统一分配内存池中的内存,在适当的时候释放内存池的内存(如关闭http链接时调用ngx_destroy_pool进行销毁)。

因此,开发者只需在需要内存时进行申请即可,不用过多考虑内存的释放等问题,大大提高了开发的效率。先看一下内存池结构。

1.1 ngx_pool_t结构

此处统一一下概念,内存池的数据块:即分配内存在这些数据块中进行,一个内存池可以有多一个内存池数据块。nginx的内存池结构如下。

00048: typedef struct {
00049: u_char *last; //当前内存池分配到此处,即下一次分配从此处开始
00050: u_char *end; //内存池结束位置
00051: ngx_pool_t *next; //内存池里面有很多块内存,这些内存块就是通过该指针连成链表的
00052: ngx_uint_t failed; //内存池分配失败次数
00053: } ngx_pool_data_t; //内存池的数据块位置信息
00054:
00055:
00056: struct ngx_pool_s{ //内存池头部结构
00057: ngx_pool_data_t d; //内存池的数据块
00058: size_t max; //内存池数据块的最大值
00059: ngx_pool_t *current; //指向当前内存池
00060: ngx_chain_t *chain; //该指针挂接一个ngx_chain_t结构
00061: ngx_pool_large_t *large; //大块内存链表,即分配空间超过max的内存
00062: ngx_pool_cleanup_t *cleanup; //释放内存池的callback
00063: ngx_log_t *log; //日志信息
00064: };

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

00048:
typedef struct
{

00049:  
u_char      *last;  //当前内存池分配到此处,即下一次分配从此处开始
00050:  
u_char      *end;  
//内存池结束位置

00051:  
ngx_pool_t  *next;  //内存池里面有很多块内存,这些内存块就是通过该指针连成链表的
00052:  
ngx_uint_t  failed;
//内存池分配失败次数

00053:
} ngx_pool_data_t;    //内存池的数据块位置信息
00054:

00055:
00056:
struct ngx_pool_s{    //内存池头部结构

00057:    ngx_pool_data_t    
d;      
//内存池的数据块
00058:    size_t              max;    
//内存池数据块的最大值

00059:    ngx_pool_t         *current;
//指向当前内存池
00060:    ngx_chain_t        *chain;  
//该指针挂接一个ngx_chain_t结构

00061:    ngx_pool_large_t   *large;  
//大块内存链表,即分配空间超过max的内存
00062:    ngx_pool_cleanup_t *cleanup;
//释放内存池的callback

00063:    ngx_log_t          *log;    
//日志信息
00064:
};

其中,sizeof(ngx_pool_data_t)=16B,sizeof(ngx_pool_t)=40B。
nginx将几乎所有的结构体放在ngx_core.h文件中重新进行了申明,如下。

typedef struct ngx_module_s ngx_module_t;
typedef struct ngx_conf_s ngx_conf_t;
typedef struct ngx_cycle_s ngx_cycle_t;
typedef struct ngx_pool_s ngx_pool_t;
typedef struct ngx_chain_s ngx_chain_t;
typedef struct ngx_log_s ngx_log_t;
typedef struct ngx_array_s ngx_array_t;
typedef struct ngx_open_file_s ngx_open_file_t;
typedef struct ngx_command_s ngx_command_t;
typedef struct ngx_file_s ngx_file_t;
typedef struct ngx_event_s ngx_event_t;
typedef struct ngx_event_aio_s ngx_event_aio_t;
typedef struct ngx_connection_s ngx_connection_t;

1
2
3
4
5
6
7
8
9
10
11
12
13

typedef
struct ngx_module_s      ngx_module_t;

typedef struct
ngx_conf_s        ngx_conf_t;
typedef
struct ngx_cycle_s      
ngx_cycle_t;

typedef struct
ngx_pool_s        ngx_pool_t;
typedef
struct ngx_chain_s      
ngx_chain_t;

typedef struct
ngx_log_s         ngx_log_t;
typedef
struct ngx_array_s      
ngx_array_t;

typedef struct
ngx_open_file_s   ngx_open_file_t;
typedef
struct ngx_command_s    
ngx_command_t;

typedef struct
ngx_file_s        ngx_file_t;
typedef
struct ngx_event_s      
ngx_event_t;

typedef struct
ngx_event_aio_s   ngx_event_aio_t;
typedef
struct ngx_connection_s  ngx_connection_t;

1.2 其他相关结构

其他与内存池相干的数据结构,如清除资源的cleanup链表,分配的大块内存链表等,如下。

00015: /*
00016: * NGX_MAX_ALLOC_FROM_POOL should be (ngx_pagesize - 1), i.e. 4095 on x86.
00017: * On Windows NT it decreases a number of locked pages in a kernel.
00018: */
00019: #define NGX_MAX_ALLOC_FROM_POOL (ngx_pagesize - 1) //在x86体系结构下,该值一般为4096B,即4K
00020:
00021: #define NGX_DEFAULT_POOL_SIZE (16* 1024)
00022:
00023: #define NGX_POOL_ALIGNMENT 16
00024: #define NGX_MIN_POOL_SIZE \
00025: ngx_align((sizeof(ngx_pool_t) + 2 * sizeof(ngx_pool_large_t)), \
00026: NGX_POOL_ALIGNMENT)
00027:
00028:
00029: typedef void (*ngx_pool_cleanup_pt)(void *data); //cleanup的callback类型
00030:
00031: typedef struct ngx_pool_cleanup_s ngx_pool_cleanup_t;
00032:
00033: struct ngx_pool_cleanup_s{
00034: ngx_pool_cleanup_pt handler;
00035: void *data; //指向要清除的数据
00036: ngx_pool_cleanup_t *next; //下一个cleanup callback
00037: };
00038:
00039:
00040: typedef struct ngx_pool_large_s ngx_pool_large_t;
00041:
00042: struct ngx_pool_large_s{
00043: ngx_pool_large_t *next; //指向下一块大块内存
00044: void *alloc; //指向分配的大块内存
00045: };
...
...
00067: typedef struct {
00068: ngx_fd_t fd;
00069: u_char *name;
00070: ngx_log_t *log;
00071: } ngx_pool_cleanup_file_t;
00072:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

00015:
/*

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

00018: */
00019:
#define NGX_MAX_ALLOC_FROM_POOL (ngx_pagesize - 1)  //在x86体系结构下,该值一般为4096B,即4K

00020:
00021:
#define NGX_DEFAULT_POOL_SIZE    (16* 1024)

00022:
00023:
#define NGX_POOL_ALIGNMENT    16

00024:
#define NGX_MIN_POOL_SIZE    \
00025:    ngx_align((sizeof(ngx_pool_t)
+ 2
* sizeof(ngx_pool_large_t)),    \

00026:    NGX_POOL_ALIGNMENT)
00027:

00028:
00029:
typedef void
(*ngx_pool_cleanup_pt)(void
*data);    //cleanup的callback类型

00030:
00031:
typedef struct
ngx_pool_cleanup_s ngx_pool_cleanup_t;

00032:
00033:
struct ngx_pool_cleanup_s{

00034:    ngx_pool_cleanup_pt
handler;
00035:    void    *data;              //指向要清除的数据

00036:    ngx_pool_cleanup_t *next;  
//下一个cleanup callback
00037:
};

00038:
00039:

00040:
typedef struct
ngx_pool_large_s ngx_pool_large_t;
00041:

00042:
struct ngx_pool_large_s{
00043:    ngx_pool_large_t  *next;    //指向下一块大块内存

00044:    void    *alloc;            
//指向分配的大块内存
00045:
};

...
...

00067:
typedef struct
{
00068:    ngx_fd_t  
fd;

00069:    u_char    *name;
00070:    ngx_log_t *log;

00071:
} ngx_pool_cleanup_file_t;
00072:

(gdb) p getpagesize()

$18 = 4096

全局变量ngx_pagesize的初始化是在如下函数中完成的。./src/os/unix/ngx_posix_init.c

ngx_int_t
ngx_os_init(ngx_log_t *log)
{
ngx_uint_t n;

#if (NGX_HAVE_OS_SPECIFIC_INIT)
if (ngx_os_specific_init(log) != NGX_OK) {
return NGX_ERROR;
}
#endif

ngx_init_setproctitle(log);

/** 该函数为glibc的库函数,由系统调用实现,返回内核中的PAGE_SIZE,该值依赖体系结构*/
ngx_pagesize = getpagesize();
ngx_cacheline_size = NGX_CPU_CACHE_LINE;
...
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

ngx_int_t

ngx_os_init(ngx_log_t *log)
{

    ngx_uint_t  n;
 

#if (NGX_HAVE_OS_SPECIFIC_INIT)
    if
(ngx_os_specific_init(log)
!= NGX_OK)
{

        return
NGX_ERROR;
    }

#endif
 

    ngx_init_setproctitle(log);
 

    /** 该函数为glibc的库函数,由系统调用实现,返回内核中的PAGE_SIZE,该值依赖体系结构*/
    ngx_pagesize
= getpagesize();      

    ngx_cacheline_size
= NGX_CPU_CACHE_LINE;
    ...

}

这些数据结构之间的关系,请参考后面的图。

1.3 ngx_pool_t的逻辑结构

这些数据结构逻辑结构图如下。注:本文采用UML的方式画出该图。





2. 内存池操作

2.1 创建内存池

创建内存池有ngx_create_pool()函数完成,代码如下。

00015: ngx_pool_t *
00016: ngx_create_pool(size_t size, ngx_log_t *log)
00017: {
00018: ngx_pool_t *p;
00019:
00020: p = ngx_memalign(NGX_POOL_ALIGNMENT, size, log);
00021: if (p == NULL) {
00022: return NULL;
00023: }
00024:
00025: p->d.last = (u_char *) p + sizeof(ngx_pool_t); //last指向ngx_pool_t结构体之后数据取起始位置
00026: p->d.end = (u_char *) p + size; //end指向分配的整个size大小的内存的末尾
00027: p->d.next = NULL;
00028: p->d.failed = 0;
00029:
00030: size = size - sizeof(ngx_pool_t);
00031: p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL; //最大不超过4095B
00032:
00033: p->current = p;
00034: p->chain = NULL;
00035: p->large = NULL;
00036: p->cleanup = NULL;
00037: p->log = log;
00038:
00039: return p;
00040: }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

00015:
ngx_pool_t *

00016:
ngx_create_pool(size_t
size,
ngx_log_t *log)
00017:
{

00018:    ngx_pool_t *p;
00019:

00020:    p
= ngx_memalign(NGX_POOL_ALIGNMENT,
size,
log);
00021:    if
(p
== NULL)
{

00022:      
return NULL;
00023:    }

00024:
00025:    p->d.last
= (u_char *)
p +
sizeof(ngx_pool_t);  //last指向ngx_pool_t结构体之后数据取起始位置

00026:    p->d.end
= (u_char *)
p +
size;  //end指向分配的整个size大小的内存的末尾
00027:    p->d.next
= NULL;

00028:    p->d.failed
= 0;
00029:

00030:    size
= size
- sizeof(ngx_pool_t);
00031:    p->max
= (size
< NGX_MAX_ALLOC_FROM_POOL)
? size
: NGX_MAX_ALLOC_FROM_POOL;  //最大不超过4095B

00032:
00033:    p->current
= p;

00034:    p->chain
= NULL;
00035:    p->large
= NULL;

00036:    p->cleanup
= NULL;
00037:    p->log
= log;

00038:
00039:    return
p;

00040:
}

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





2.2 销毁内存池

销毁内存池由如下函数完成。

void ngx_destroy_pool(ngx_pool_t *pool)

该函数将遍历内存池链表,所有释放内存,如果注册了clenup(也是一个链表结构),亦将遍历该cleanup链表结构依次调用clenup的handler清理。同时,还将遍历large链表,释放大块内存。

2.3 重置内存池

重置内存池由下面的函数完成。

void ngx_reset_pool(ngx_pool_t *pool);

该函数将释放所有large内存,并且将d->last指针重新指向ngx_pool_t结构之后数据区的开始位置,同刚创建后的位置相同。

2.4 分配内存

内存分配的函数如下。

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

返回值为分配的内存起始地址。选择其中的两个函数进行分析,其他的也很好理解,省略。

2.4.1 ngx_palloc()函数分析

ngx_palloc()代码如下,分析请参考笔者所加的注释。

00115: void *
00116: ngx_palloc(ngx_pool_t *pool, size_t size)
00117: {
00118: u_char *m;
00119: ngx_pool_t *p;
00120:
00121: if (size <= pool->max) {//判断待分配内存与max值
00122:
00123: p = pool->current; //小于max值,则从current节点开始遍历pool链表
00124:
00125: do {
00126: m = ngx_align_ptr(p->d.last, NGX_ALIGNMENT);
00127:
00128: if ((size_t) (p->d.end - m) >= size) {
00129: p->d.last = m + size; //在该节点指向的内存块中分配size大小的内存
00130:
00131: return m;
00132: }
00133:
00134: p = p->d.next;
00135:
00136: } while (p);
00137:
00138: return ngx_palloc_block(pool, size); //链表里没有能分配size大小内存的节点,则生成一个新的节点并在其中分配内存
00139: }
00140:
00141: return ngx_palloc_large(pool, size); //大于max值,则在large链表里分配内存
00142: }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

00115:
void *

00116:
ngx_palloc(ngx_pool_t *pool,
size_t size)
00117:
{

00118:    u_char    *m;
00119:    ngx_pool_t *p;

00120:
00121:    if
(size
<= pool->max)
{//判断待分配内存与max值

00122:
00123:      
p =
pool->current;  
//小于max值,则从current节点开始遍历pool链表

00124:
00125:      
do {

00126:          m
= ngx_align_ptr(p->d.last,
NGX_ALIGNMENT);
00127:

00128:          if
((size_t)
(p->d.end
- m)
>= size)
{
00129:            
p->d.last
= m
+ size;  //在该节点指向的内存块中分配size大小的内存

00130:
00131:            
return m;

00132:          }
00133:

00134:          p
= p->d.next;
00135:

00136:      
} while
(p);
00137:

00138:      
return ngx_palloc_block(pool,
size);
//链表里没有能分配size大小内存的节点,则生成一个新的节点并在其中分配内存
00139:    }

00140:
00141:    return
ngx_palloc_large(pool,
size);  //大于max值,则在large链表里分配内存

00142:
}

例如,在2.1节中创建的内存池中分配200B的内存,调用ngx_palloc(pool, 200)后,该内存池物理结构如下图。





2.4.2 ngx_palloc_block()函数分析

ngx_palloc_block函数代码如下,分析请参考笔者所加的注释。

00175: static void *
00176: ngx_palloc_block(ngx_pool_t *pool, size_t size)
00177: {
00178: u_char *m;
00179: size_t psize;
00180: ngx_pool_t *p, *new, *current;
00181:
00182: psize = (size_t) (pool->d.end - (u_char *) pool); //计算pool的大小
00183:
00184: m = ngx_memalign(NGX_POOL_ALIGNMENT, psize, pool->log);//分配一块与pool大小相同的内存
00185: if (m == NULL) {
00186: return NULL;
00187: }
00188:
00189: new = (ngx_pool_t *) m;
00190:
00191: new->d.end = m + psize; //设置end指针
00192: new->d.next = NULL;
00193: new->d.failed = 0;
00194:
00195: m += sizeof(ngx_pool_data_t); //让m指向该块内存ngx_pool_data_t结构体之后数据区起始位置
00196: m = ngx_align_ptr(m, NGX_ALIGNMENT); //按4字节对齐
00197: new->d.last = m + size; //在数据区分配size大小的内存并设置last指针
00198:
00199: current = pool->current;
00200:
00201: for (p = current; p->d.next; p = p->d.next) {
00202: if (p->d.failed++ > 4) { //failed的值只在此处被修改
00203: current = p->d.next; //失败4次以上移动current指针
00204: }
00205: }
00206:
00207: p->d.next = new; //将这次分配的内存块new加入该内存池
00208:
00209: pool->current = current ? current : new;
00210:
00211: return m;
00212: }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

00175:
static void
*

00176:
ngx_palloc_block(ngx_pool_t *pool,
size_t size)
00177:
{

00178:    u_char    *m;
00179:    size_t    psize;

00180:    ngx_pool_t *p,
*new,
*current;
00181:

00182:    psize
= (size_t)
(pool->d.end
- (u_char *)
pool);      //计算pool的大小
00183:

00184:    m
= ngx_memalign(NGX_POOL_ALIGNMENT,
psize,
pool->log);//分配一块与pool大小相同的内存
00185:    if
(m
== NULL)
{

00186:      
return NULL;
00187:    }

00188:
00189:    new
= (ngx_pool_t *)
m;

00190:
00191:    new->d.end
= m
+ psize;
//设置end指针

00192:    new->d.next
= NULL;
00193:    new->d.failed
= 0;

00194:
00195:    m
+= sizeof(ngx_pool_data_t);
//让m指向该块内存ngx_pool_data_t结构体之后数据区起始位置

00196:    m
= ngx_align_ptr(m,
NGX_ALIGNMENT);
//按4字节对齐
00197:    new->d.last
= m
+ size;      
//在数据区分配size大小的内存并设置last指针

00198:
00199:    current
= pool->current;

00200:
00201:    for
(p
= current;
p->d.next;
p =
p->d.next)
{

00202:      
if (p->d.failed++
> 4)
{   //failed的值只在此处被修改
00203:          current
= p->d.next;    //失败4次以上移动current指针

00204:      
}
00205:    }

00206:
00207:    p->d.next
= new;  //将这次分配的内存块new加入该内存池

00208:
00209:    pool->current
= current
? current
: new;

00210:
00211:    return
m;

00212:
}

注意:该函数分配一块内存后,last指针指向的是ngx_pool_data_t结构体(大小16B)之后数据区的起始位置。而创建内存池时时,last指针指向的是ngx_pool_t结构体(大小40B)之后数据区的起始位置。

结合2.7节的内存池的物理结构,更容易理解。

2.5 释放内存

请参考如下函数,不再赘述。

ngx_int_t ngx_pfree(ngx_pool_t *pool, void *p)

需要注意的是该函数只释放large链表中注册的内存,普通内存在ngx_destroy_pool中统一释放。

2.6 注册cleanup

请参考如下函数,该函数实现也很简单,此处不再赘述。

ngx_pool_cleanup_t *ngx_pool_cleanup_add(ngx_pool_t *p, size_t size)

2.7 内存池的物理结构

针对本文第3节的例子,画出的内存池的物理结构如下图。





从该图也能看出2.4节的结论,即内存池第一块内存前40字节为ngx_pool_t结构,后续加入的内存块前16个字节为ngx_pool_data_t结构,这两个结构之后便是真正可以分配内存区域。

因此,本文Reference中的内存分配相关中的图是有一点点小问题的,并不是每一个节点的前面都是ngx_pool_t结构。

3. 一个例子

理解并掌握开源软件的最好方式莫过于自己写一些测试代码,或者改写软件本身,并进行调试来进一步理解开源软件的原理和设计方法。本节给出一个创建内存池并从中分配内存的简单例子。

3.1 代码

/**
* ngx_pool_t test, to test ngx_palloc, ngx_palloc_block, ngx_palloc_large
*/

#include <stdio.h>
#include "ngx_config.h"
#include "ngx_conf_file.h"
#include "nginx.h"
#include "ngx_core.h"
#include "ngx_string.h"
#include "ngx_palloc.h"

volatile ngx_cycle_t *ngx_cycle;

void ngx_log_error_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,
const char *fmt, ...)
{
}

void dump_pool(ngx_pool_t* pool)
{
while (pool)
{
printf("pool = 0x%x\n", pool);
printf(" .d\n");
printf(" .last = 0x%x\n", pool->d.last);
printf(" .end = 0x%x\n", pool->d.end);
printf(" .next = 0x%x\n", pool->d.next);
printf(" .failed = %d\n", pool->d.failed);
printf(" .max = %d\n", pool->max);
printf(" .current = 0x%x\n", pool->current);
printf(" .chain = 0x%x\n", pool->chain);
printf(" .large = 0x%x\n", pool->large);
printf(" .cleanup = 0x%x\n", pool->cleanup);
printf(" .log = 0x%x\n", pool->log);
printf("available pool memory = %d\n\n", pool->d.end - pool->d.last);
pool = pool->d.next;
}
}

int main()
{
ngx_pool_t *pool;

printf("--------------------------------\n");
printf("create a new pool:\n");
printf("--------------------------------\n");
pool = ngx_create_pool(1024, NULL);
dump_pool(pool);

printf("--------------------------------\n");
printf("alloc block 1 from the pool:\n");
printf("--------------------------------\n");
ngx_palloc(pool, 512);
dump_pool(pool);

printf("--------------------------------\n");
printf("alloc block 2 from the pool:\n");
printf("--------------------------------\n");
ngx_palloc(pool, 512);
dump_pool(pool);

printf("--------------------------------\n");
printf("alloc block 3 from the pool :\n");
printf("--------------------------------\n");
ngx_palloc(pool, 512);
dump_pool(pool);

ngx_destroy_pool(pool);
return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71

/**

* ngx_pool_t test, to test ngx_palloc, ngx_palloc_block, ngx_palloc_large
*/

 
#include <stdio.h>

#include "ngx_config.h"
#include "ngx_conf_file.h"

#include "nginx.h"
#include "ngx_core.h"

#include "ngx_string.h"
#include "ngx_palloc.h"

 
volatile
ngx_cycle_t  *ngx_cycle;

 
void
ngx_log_error_core(ngx_uint_t
level,
ngx_log_t *log,
ngx_err_t err,

            const
char *fmt,
...)
{

}
 

void dump_pool(ngx_pool_t*
pool)
{

    while
(pool)
    {

        printf("pool = 0x%x\n",
pool);
        printf("  .d\n");

        printf("    .last = 0x%x\n",
pool->d.last);
        printf("    .end = 0x%x\n",
pool->d.end);

        printf("    .next = 0x%x\n",
pool->d.next);
        printf("    .failed = %d\n",
pool->d.failed);

        printf("  .max = %d\n",
pool->max);
        printf("  .current = 0x%x\n",
pool->current);

        printf("  .chain = 0x%x\n",
pool->chain);
        printf("  .large = 0x%x\n",
pool->large);

        printf("  .cleanup = 0x%x\n",
pool->cleanup);
        printf("  .log = 0x%x\n",
pool->log);

        printf("available pool memory = %d\n\n",
pool->d.end
- pool->d.last);
        pool
= pool->d.next;

    }
}

 
int
main()

{
    ngx_pool_t *pool;

 
    printf("--------------------------------\n");

    printf("create a new pool:\n");
    printf("--------------------------------\n");

    pool
= ngx_create_pool(1024,
NULL);
    dump_pool(pool);

 
    printf("--------------------------------\n");

    printf("alloc block 1 from the pool:\n");
    printf("--------------------------------\n");

    ngx_palloc(pool,
512);
    dump_pool(pool);

 
    printf("--------------------------------\n");

    printf("alloc block 2 from the pool:\n");
    printf("--------------------------------\n");

    ngx_palloc(pool,
512);
    dump_pool(pool);

 
    printf("--------------------------------\n");

    printf("alloc block 3 from the pool :\n");
    printf("--------------------------------\n");

    ngx_palloc(pool,
512);
    dump_pool(pool);

 
    ngx_destroy_pool(pool);

    return
0;
}

3.2 如何编译

这个问题是编写测试代码或者改写软件本身最迫切需要解决的问题,否则,编写的代码无从编译或运行,那也无从进行调试并理解软件了。我们要做的是学习这种编译工程的方法,针对该例子,笔者编写的makefile文件如下。

CXX = gcc
CXXFLAGS += -g -Wall -Wextra

NGX_ROOT = /usr/src/nginx-1.0.4

TARGETS = ngx_pool_t_test
TARGETS_C_FILE = $(TARGETS).c

CLEANUP = rm -f $(TARGETS) *.o

all: $(TARGETS)

clean:
$(CLEANUP)

CORE_INCS = -I. \
-I$(NGX_ROOT)/src/core \
-I$(NGX_ROOT)/src/event \
-I$(NGX_ROOT)/src/event/modules \
-I$(NGX_ROOT)/src/os/unix \
-I$(NGX_ROOT)/objs \

NGX_PALLOC = $(NGX_ROOT)/objs/src/core/ngx_palloc.o
NGX_STRING = $(NGX_ROOT)/objs/src/core/ngx_string.o
NGX_ALLOC = $(NGX_ROOT)/objs/src/os/unix/ngx_alloc.o

$(TARGETS): $(TARGETS_C_FILE)
$(CXX) $(CXXFLAGS) $(CORE_INCS) $(NGX_PALLOC) $(NGX_STRING) $(NGX_ALLOC) $^ -o $@

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

CXX
= gcc

CXXFLAGS +=
-g
-Wall
-Wextra
 

NGX_ROOT =
/usr/src/nginx-1.0.4
 

TARGETS =
ngx_pool_t_test
TARGETS_C_FILE
= $(TARGETS).c

 
CLEANUP
= rm
-f
$(TARGETS)
*.o

 
all:
$(TARGETS)

 
clean:

    $(CLEANUP)
 

CORE_INCS =
-I.
\
    -I$(NGX_ROOT)/src/core
\

    -I$(NGX_ROOT)/src/event
\
    -I$(NGX_ROOT)/src/event/modules
\

    -I$(NGX_ROOT)/src/os/unix
\
    -I$(NGX_ROOT)/objs
\

 
NGX_PALLOC
= $(NGX_ROOT)/objs/src/core/ngx_palloc.o

NGX_STRING =
$(NGX_ROOT)/objs/src/core/ngx_string.o
NGX_ALLOC
= $(NGX_ROOT)/objs/src/os/unix/ngx_alloc.o

 
$(TARGETS):
$(TARGETS_C_FILE)

    $(CXX)
$(CXXFLAGS)
$(CORE_INCS)
$(NGX_PALLOC)
$(NGX_STRING)
$(NGX_ALLOC)
$^
-o
$@

3.3 运行运行结果

# ./ngx_pool_t_test
--------------------------------
create a new pool:
--------------------------------
pool = 0x8922020
.d
.last = 0x8922048
.end = 0x8922420
.next = 0x0
.failed = 0
.max = 984
.current = 0x8922020
.chain = 0x0
.large = 0x0
.cleanup = 0x0
.log = 0x0
available pool memory = 984

--------------------------------
alloc block 1 from the pool:
--------------------------------
pool = 0x8922020
.d
.last = 0x8922248
.end = 0x8922420
.next = 0x0
.failed = 0
.max = 984
.current = 0x8922020
.chain = 0x0
.large = 0x0
.cleanup = 0x0
.log = 0x0
available pool memory = 472

--------------------------------
alloc block 2 from the pool:
--------------------------------
pool = 0x8922020
.d
.last = 0x8922248
.end = 0x8922420
.next = 0x8922450
.failed = 0
.max = 984
.current = 0x8922020
.chain = 0x0
.large = 0x0
.cleanup = 0x0
.log = 0x0
available pool memory = 472

pool = 0x8922450
.d
.last = 0x8922660
.end = 0x8922850
.next = 0x0
.failed = 0
.max = 0
.current = 0x0
.chain = 0x0
.large
13e14
= 0x0
.cleanup = 0x0
.log = 0x0
available pool memory = 496

--------------------------------
alloc block 3 from the pool :
--------------------------------
pool = 0x8922020
.d
.last = 0x8922248
.end = 0x8922420
.next = 0x8922450
.failed = 1
.max = 984
.current = 0x8922020
.chain = 0x0
.large = 0x0
.cleanup = 0x0
.log = 0x0
available pool memory = 472

pool = 0x8922450
.d
.last = 0x8922660
.end = 0x8922850
.next = 0x8922880
.failed = 0
.max = 0
.current = 0x0
.chain = 0x0
.large = 0x0
.cleanup = 0x0
.log = 0x0
available pool memory = 496

pool = 0x8922880
.d
.last = 0x8922a90
.end = 0x8922c80
.next = 0x0
.failed = 0
.max = 0
.current = 0x0
.chain = 0x0
.large = 0x0
.cleanup = 0x0
.log = 0x0
available pool memory = 496

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110

# ./ngx_pool_t_test

--------------------------------
create
a new
pool:

--------------------------------
pool
= 0x8922020

  .d
    .last
= 0x8922048

    .end
= 0x8922420
    .next
= 0x0

    .failed
= 0
  .max
= 984

  .current
= 0x8922020
  .chain
= 0x0

  .large
= 0x0
  .cleanup
= 0x0

  .log
= 0x0
available
pool memory
= 984

 
--------------------------------

alloc block
1 from
the pool:
--------------------------------

pool =
0x8922020
  .d

    .last
= 0x8922248
    .end
= 0x8922420

    .next
= 0x0
    .failed
= 0

  .max
= 984
  .current
= 0x8922020

  .chain
= 0x0
  .large
= 0x0

  .cleanup
= 0x0
  .log
= 0x0

available pool memory
= 472
 

--------------------------------
alloc
block 2
from the pool:

--------------------------------
pool
= 0x8922020

  .d
    .last
= 0x8922248

    .end
= 0x8922420
    .next
= 0x8922450

    .failed
= 0
  .max
= 984

  .current
= 0x8922020
  .chain
= 0x0

  .large
= 0x0
  .cleanup
= 0x0

  .log
= 0x0
available
pool memory
= 472

 
pool
= 0x8922450

  .d
    .last
= 0x8922660

    .end
= 0x8922850
    .next
= 0x0

    .failed
= 0
  .max
= 0

  .current
= 0x0
  .chain
= 0x0

  .large
= 0x0
  .cleanup
= 0x0

  .log
= 0x0
available
pool memory
= 496

 
--------------------------------

alloc block
3 from
the pool
:
--------------------------------

pool =
0x8922020
  .d

    .last
= 0x8922248
    .end
= 0x8922420

    .next
= 0x8922450
    .failed
= 1

  .max
= 984
  .current
= 0x8922020

  .chain
= 0x0
  .large
= 0x0

  .cleanup
= 0x0
  .log
= 0x0

available pool memory
= 472
 

pool =
0x8922450
  .d

    .last
= 0x8922660
    .end
= 0x8922850

    .next
= 0x8922880
    .failed
= 0

  .max
= 0
  .current
= 0x0

  .chain
= 0x0
  .large
= 0x0

  .cleanup
= 0x0
  .log
= 0x0

available pool memory
= 496
 

pool =
0x8922880
  .d

    .last
= 0x8922a90
    .end
= 0x8922c80

    .next
= 0x0
    .failed
= 0

  .max
= 0
  .current
= 0x0

  .chain
= 0x0
  .large
= 0x0

  .cleanup
= 0x0
  .log
= 0x0

available pool memory
= 496

 4. 小结

本文针对nginx-1.0.4的内存管理进行了较为全面的分析,包括相关内存池数据结构,内存池的创建、销毁,以及从内存池中分配内存等。最后通过一个简单例子向读者展示nginx内存池的创建和分配操作,同时借此向读者展示编译测试代码的方法。

分析完nginx的内存管理,你一定惊叹于nginx作者的聪明才智。这种内存管理的设计方法小巧、快捷,值得借鉴!

系列文章:

从开源代码Nginx中学习编码风格

nginx架构初探
Nginx高效数据结构(1)——数组(ngx_array_t)

Nginx高效数据结构(2)——链表(ngx_list_t)

Nginx高效数据结构(3)——队列(ngx_queue_t)

Nginx高效数据结构(4)——Hash表(ngx_hash_t)

Nginx高效数据结构(5)——内存池(ngx_pool_t)

作者:阿波

EDITED BY:快课(www.cricode.com)

本文链接:http://cricode.com/2999.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: