由于本人才疏学浅,本文难免存在遗漏之处,欢迎大家留言指正,本人将感激不尽。
Nginx使用内存池对内存进行管理,提高内存管理效率。对于小块内存,直接从已分配好的内存池中获取(返回指针);对于大块内存,直接调用malloc申请内存,然后将大块内存挂在内存池上进行管理(链表形式)。
对于内存的申请,Nginx还做了一些优化,比如在某一块内存池上申请小内存失败次数超过四次时,直接跳过该内存池,后续将不在该内存池上申请小块内存;
Nginx内存管理的核心结构体如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 1typedef struct {
2 u_char *last; //下一次申请小块内存的起始地址
3 u_char *end; //当前内存池的结束地址
4 ngx_pool_t *next; //指向下一个内存池
5 ngx_uint_t failed; //小块内存申请失败的次数
6} ngx_pool_data_t;
7
8struct ngx_pool_s { //内存池结构体
9 ngx_pool_data_t d; //管理小块内存
10 size_t max; //区分大块内存和小块内存
11 ngx_pool_t *current; //下一次申请小块内存的pool
12 ngx_chain_t *chain;
13 ngx_pool_large_t *large; //保存大块内存的链表
14 ngx_pool_cleanup_t *cleanup;
15 ngx_log_t *log;
16};
17
18
对于小块内存,全部位于ngx_pool_s的数据区段,ngx_pool_s按链表形式组织;对于大块内存,全部以链表的形式存储,large指向分配的第一个大块内存。如下图所示:

下面主要通过代码介绍一下内存分配的过程
创建内存池
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 1ngx_pool_t *
2ngx_create_pool(size_t size, ngx_log_t *log)
3{
4 ngx_pool_t *p;
5
6 p = ngx_memalign(NGX_POOL_ALIGNMENT, size, log);
7 if (p == NULL) {
8 return NULL;
9 }
10
11 p->d.last = (u_char *) p + sizeof(ngx_pool_t);
12 p->d.end = (u_char *) p + size;
13 p->d.next = NULL;
14 p->d.failed = 0;
15
16 size = size - sizeof(ngx_pool_t);
17 p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL;
18
19 p->current = p;
20 p->chain = NULL;
21 p->large = NULL;
22 p->cleanup = NULL;
23 p->log = log;
24
25 return p;
26}
27
28
分配内存如下:
1
2
3
4
5
6
7
8
9
10
11
12
13 1void *
2ngx_palloc(ngx_pool_t *pool, size_t size)
3{
4#if !(NGX_DEBUG_PALLOC)
5 if (size <= pool->max) { //分配小块内存,直接在内存池上分配,移动指针
6 return ngx_palloc_small(pool, size, 1);
7 }
8#endif
9
10 return ngx_palloc_large(pool, size); //分配大块内存,调用malloc创建size大小的内存,然后插入large链表
11}
12
13
分配小块内存如下:
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 1static ngx_inline void *
2ngx_palloc_small(ngx_pool_t *pool, size_t size, ngx_uint_t align)
3{
4 u_char *m;
5 ngx_pool_t *p;
6
7 p = pool->current; //获取当前可申请小块内存的pool
8
9 /*在内存池链表中查找可分配小块内存的内存池*/
10 do {
11 m = p->d.last;
12 if (align) {
13 m = ngx_align_ptr(m, NGX_ALIGNMENT);
14 }
15 if ((size_t) (p->d.end - m) >= size) {
16 p->d.last = m + size;
17 return m;
18 }
19 p = p->d.next;
20 } while (p);
21
22 return ngx_palloc_block(pool, size); //内存池链表中未找到可分配小块内存的内存池,则创建新的内存池
23}
24
25
26static void *
27ngx_palloc_block(ngx_pool_t *pool, size_t size)
28{
29 u_char *m;
30 size_t psize;
31 ngx_pool_t *p, *new;
32
33 psize = (size_t) (pool->d.end - (u_char *) pool);
34
35 m = ngx_memalign(NGX_POOL_ALIGNMENT, psize, pool->log);
36 if (m == NULL) {
37 return NULL;
38 }
39
40 new = (ngx_pool_t *) m;
41
42 new->d.end = m + psize;
43 new->d.next = NULL;
44 new->d.failed = 0;
45
46 m += sizeof(ngx_pool_data_t);
47 m = ngx_align_ptr(m, NGX_ALIGNMENT);
48 new->d.last = m + size;
49
50 for (p = pool->current; p->d.next; p = p->d.next) {
51 if (p->d.failed++ > 4) { //如果超过四次分配小内存失败,则将current指针后移
52 pool->current = p->d.next;
53 }
54 }
55
56 p->d.next = new; //将新创建的内存池加入内存池链表尾部
57
58 return m;
59}
60
61
分配大块内存如下:只有第一个内存池拥有大块内存链表
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 1static void *
2ngx_palloc_large(ngx_pool_t *pool, size_t size)
3{
4 void *p;
5 ngx_uint_t n;
6 ngx_pool_large_t *large;
7
8 p = ngx_alloc(size, pool->log); //申请大块内存
9 if (p == NULL) {
10 return NULL;
11 }
12
13 n = 0;
14
15 //插入large链表中,寻找可插入的位置
16 for (large = pool->large; large; large = large->next) {
17 if (large->alloc == NULL) {
18 large->alloc = p;
19 return p;
20 }
21
22 if (n++ > 3) { //若连续三次未找到可插入的位置,则直接插入链表头部
23 break;
24 }
25 }
26
27 large = ngx_palloc_small(pool, sizeof(ngx_pool_large_t), 1);
28 if (large == NULL) {
29 ngx_free(p);
30 return NULL;
31 }
32
33 //插入链表头部
34 large->alloc = p;
35 large->next = pool->large;
36 pool->large = large;
37
38 return p;
39}
40
41