Nginx内存池管理

释放双眼,带上耳机,听听看~!

由于本人才疏学浅,本文难免存在遗漏之处,欢迎大家留言指正,本人将感激不尽。

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指向分配的第一个大块内存。如下图所示:
Nginx内存池管理
下面主要通过代码介绍一下内存分配的过程

创建内存池


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

参考

https://www.kancloud.cn/digest/understandingnginx/202588

给TA打赏
共{{data.count}}人
人已打赏
安全运维

WordPress网站专用docker容器环境带Waf

2020-7-18 20:04:44

安全运维

运维安全-Gitlab管理员权限安全思考

2021-9-19 9:16:14

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索