TheRiver | blog

You have reached the world's edge, none but devils play past here

0%

nginx源码分析-内存池

参考

Nginx源代码情景分析(3)——Nginx内存管理-1

Nginx源码剖析之内存池,与内存管理

chronolaw/annotated_nginx


nginx_pool_t.jpg


ngx_pool_t

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

/* 内存池链表结构体 */
typedef struct ngx_pool_s ngx_pool_t;
struct ngx_pool_s {
ngx_pool_data_t d;
size_t max;
ngx_pool_t *current;
ngx_chain_t *chain;
ngx_pool_large_t *large;
ngx_pool_cleanup_t *cleanup;
ngx_log_t *log;
};


/* 小块内存的数据区结构体 */
typedef struct {
u_char *last;
u_char *end;
ngx_pool_t *next;
ngx_uint_t failed;
} ngx_pool_data_t;


/* 大块内存的链表结构体 */
typedef struct ngx_pool_large_s ngx_pool_large_t;
struct ngx_pool_large_s {
ngx_pool_large_t *next;
void *alloc;
};


ngx_create_pool

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

//创建ngx_pool_t内存链表的函数
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);
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);
//内存页大小,4096字节
p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL;

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

return p;
}


//在main函数中
ngx_cycle_t *cycle, init_cycle;
init_cycle.pool = ngx_create_pool(1024, log)
//进程启动阶段init_cycle中保存了最早申请的内存池,这时候ngx_poll_t中的date的last的值应该还是没有申请后未使用的.

//ngx_memalign函数用于申请小块内存,内部也是调用memalign,具体见下面对此函数的解释
void *
ngx_memalign(size_t alignment, size_t size, ngx_log_t *log)
{
void *p;

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

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

return p;
}



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

init_cycle.pool = (ngx_pool_t *) 0x6d2420
init_cycle.pool.d = {last = 0x6d2470 "", end = 0x6d2820 "", next = 0x0, failed = 0}
init_cycle.pool =
{ d = {last = 0x6d2470 "", end = 0x6d2820 "", next = 0x0, failed = 0},
max = 944,
current = 0x6d2420,
chain = 0x0,
large = 0x0,
cleanup = 0x0,
log = 0x6afe60 <ngx_log>
}

last - pool = 80 = sizeof(ngx_pool_t)
end - pool = 1024 = size

和ngx_create_pool函数是一致的,end指向pool+size的位置,而ngx_pool_t这80个字节是1024的前80字节,也是堆中的内存。所以实际可用的内存只有1024-80 = 944 = max。


memalign

1
2
3
4

#include <stdlib.h>
void *memalign(size_t alignment, size_t size);

The memalign() function returns a block of memory of size bytes aligned to blocksize. The blocksize must be given as a power of two. It sets errno and returns a null pointer upon failure.

函数 memalign返回按alignment个字节对齐的内存。alignment必须是2的幂。

Pointers returned by memalign() may be passed to free(). Pointers passed to realloc() are checked and if not aligned to the system, the realloc() fails and returns NULL.


ngx_destroy_pool

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

#define ngx_free free

//类似c++的析构函数,这里要释放小块,大块内存,还有自定义的"文件"
void
ngx_destroy_pool(ngx_pool_t *pool)
{
ngx_pool_t *p, *n;
ngx_pool_large_t *l;
ngx_pool_cleanup_t *c;

//释放自定义的内存,可能是文件或者套接字之类的
//handle是需要自定义实现的释放函数
for (c = pool->cleanup; c; c = c->next) {
if (c->handler) {
ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0,
"run cleanup: %p", c);
c->handler(c->data);
}
}

#if (NGX_DEBUG)

/*
* we could allocate the pool->log from this pool
* so we cannot use this log while free()ing the pool
*/

for (l = pool->large; l; l = l->next) {
ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0, "free: %p", l->alloc);
}

for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {
ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, pool->log, 0,
"free: %p, unused: %uz", p, p->d.end - p->d.last);

if (n == NULL) {
break;
}
}

#endif

//释放大块内存
for (l = pool->large; l; l = l->next) {
if (l->alloc) {
ngx_free(l->alloc);
}
}

//先释放大块内存的数据库,然后释放pool,意味着既释放了小块内存的数据区,又把结构体本身释放掉了.
//释放小块内存
for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {
//申请的时候malloc的返回值就是p,申请的1024个字节在这里就都释放了
ngx_free(p);

if (n == NULL) {
break;
}
}
}



ngx_reset_pool

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

//重置内存池,恢复到创建的初始阶段
void
ngx_reset_pool(ngx_pool_t *pool)
{
ngx_pool_t *p;
ngx_pool_large_t *l;

//删除大块内存
for (l = pool->large; l; l = l->next) {
if (l->alloc) {
ngx_free(l->alloc);
}
}

//置last为create时的位置,即小块内存数据区的起始位置
for (p = pool; p; p = p->d.next) {
p->d.last = (u_char *) p + sizeof(ngx_pool_t);
p->d.failed = 0;
}

pool->current = pool;
pool->chain = NULL;
pool->large = NULL;
}



ngx_palloc/ngx_pnalloc

ngx_palloc         按照字节对齐 
ngx_pnalloc     不按照字节对齐
即ngx_palloc_small的第三个参数不同,大块内存都是不考虑对齐的
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

// 如果编译时指定宏NGX_DEBUG_PALLOC
// 则不会启用内存池机制,都使用malloc分配内存
void *
ngx_palloc(ngx_pool_t *pool, size_t size)
{
//如果小块内存够用就申请小块内存,否则申请大块内存
#if !(NGX_DEBUG_PALLOC)
if (size <= pool->max) {
return ngx_palloc_small(pool, size, 1);
}
#endif

return ngx_palloc_large(pool, size);
}

void *
ngx_pnalloc(ngx_pool_t *pool, size_t size)
{
#if !(NGX_DEBUG_PALLOC)
if (size <= pool->max) {
return ngx_palloc_small(pool, size, 0);
}
#endif

return ngx_palloc_large(pool, size);
}



ngx_pcalloc

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

//申请内存并初始化,类似cmalloc
void *
ngx_pcalloc(ngx_pool_t *pool, size_t size)
{
void *p;

p = ngx_palloc(pool, size);
if (p) {
ngx_memzero(p, size);
}

return p;
}



ngx_palloc_small

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

static ngx_inline void *ngx_palloc_small(ngx_pool_t *pool, size_t size, ngx_uint_t align);

//
static ngx_inline void *
ngx_palloc_small(ngx_pool_t *pool, size_t size, ngx_uint_t align)
{
u_char *m;
ngx_pool_t *p;

p = pool->current;

do {
m = p->d.last;

//后面补充
if (align) {
m = ngx_align_ptr(m, NGX_ALIGNMENT);
}

//遍历链表找到空闲区域
if ((size_t) (p->d.end - m) >= size) {
p->d.last = m + size;

return m;
}

p = p->d.next;

} while (p);

return ngx_palloc_block(pool, size);
}



ngx_align_ptr

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

#define ngx_align(d, a) (((d) + (a - 1)) & ~(a - 1))
#define ngx_align_ptr(p, a) \
(u_char *) (((uintptr_t) (p) + ((uintptr_t) a - 1)) & ~((uintptr_t) a - 1))

//ngx_config.h
typedef intptr_t ngx_int_t;
typedef uintptr_t ngx_uint_t;

//上面intptr_t,uintptr_t的定义在linux系统的文件/usr/include/stdint.h
/* Types for `void *' pointers. */
#if __WORDSIZE == 64
# ifndef __intptr_t_defined
typedef long int intptr_t;
# define __intptr_t_defined
# endif
typedef unsigned long int uintptr_t;
#else
# ifndef __intptr_t_defined
typedef int intptr_t;
# define __intptr_t_defined
# endif
typedef unsigned int uintptr_t;
#endif

/*
64位系统下uintptr_t是unsigned long in
32位系统下uintptr_t是unsinged int
*/

/*
再看(((d) + (a - 1)) & ~(a - 1))这个表达式
a是2的幂(memalign返回按alignment个字节对齐的内存。alignment必须是2的幂),所以(a-1)就是二进制位右边为1,左边原来1的那位为0,再取反,然后按位与,就是把a原来右边为0的位清除掉了。保留d在a原来1的那位左边的,并且保证了这个值最小是a。所以肯定是a的倍数.
*/



ngx_palloc_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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

static void *ngx_palloc_large(ngx_pool_t *pool, size_t size);

//
static void *
ngx_palloc_large(ngx_pool_t *pool, size_t size)
{
void *p;
ngx_uint_t n;
ngx_pool_large_t *large;

//大块内存直接malloc
p = ngx_alloc(size, pool->log);
if (p == NULL) {
return NULL;
}

n = 0;

//将malloc的地址存于大块内存链表
for (large = pool->large; large; large = large->next) {
if (large->alloc == NULL) {
large->alloc = p;
return p;
}

//超过三次放弃,避免便利链表效率太低
//3可能是作者的经验值吧
if (n++ > 3) {
break;
}
}

//找不到就重新申请结构体,加入内存池链表
large = ngx_palloc_small(pool, sizeof(ngx_pool_large_t), 1);
if (large == NULL) {
ngx_free(p);
return NULL;
}

//新申请的存于链表头,头插法
large->alloc = p;
large->next = pool->large;
pool->large = large;

return p;
}



ngx_palloc_block

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

static void *ngx_palloc_block(ngx_pool_t *pool, size_t size);
//注意这个函数是在ngx_palloc_small函数中便利小块内存链表后没有找到空闲链表的情况下调用的,是用来申请新的空间挂接到链表尾的。
//不同于ngx_create_pool函数,本函数申请小块内存后last指向要+size大小
static void *
ngx_palloc_block(ngx_pool_t *pool, size_t size)
{
u_char *m;
size_t psize;
ngx_pool_t *p, *new;

psize = (size_t) (pool->d.end - (u_char *) pool);

m = ngx_memalign(NGX_POOL_ALIGNMENT, psize, pool->log);
if (m == NULL) {
return NULL;
}

new = (ngx_pool_t *) m;

new->d.end = m + psize;
new->d.next = NULL;
new->d.failed = 0;

m += sizeof(ngx_pool_data_t);
m = ngx_align_ptr(m, NGX_ALIGNMENT);
new->d.last = m + size;

//因为之前create函数已经遍历过没有找到,所以这里给failed+1
//这里应该也是经验值吧,达到5次这里把pool->current更新为p->d.next
for (p = pool->current; p->d.next; p = p->d.next) {
if (p->d.failed++ > 4) {
pool->current = p->d.next;
}
}

//新申请的内存挂到链表尾部,尾插法(和大块内存不同)
p->d.next = new;

return m;
}



ngx_pmemalign

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

//申请大块内存,并且字节对齐
void *
ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment)
{
void *p;
ngx_pool_large_t *large;

p = ngx_memalign(alignment, size, pool->log);
if (p == NULL) {
return NULL;
}

large = ngx_palloc_small(pool, sizeof(ngx_pool_large_t), 1);
if (large == NULL) {
ngx_free(p);
return NULL;
}

large->alloc = p;
large->next = pool->large;
pool->large = large;

return p;
}



ngx_pfree

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

//释放指定的大块内存
ngx_int_t
ngx_pfree(ngx_pool_t *pool, void *p)
{
ngx_pool_large_t *l;

for (l = pool->large; l; l = l->next) {
if (p == l->alloc) {
ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0,
"free: %p", l->alloc);
ngx_free(l->alloc);
l->alloc = NULL;

return NGX_OK;
}
}

return NGX_DECLINED;
}



ngx_pool_cleanup_add

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

ngx_pool_cleanup_t *
ngx_pool_cleanup_add(ngx_pool_t *p, size_t size)
{
ngx_pool_cleanup_t *c;

c = ngx_palloc(p, sizeof(ngx_pool_cleanup_t));
if (c == NULL) {
return NULL;
}

if (size) {
c->data = ngx_palloc(p, size);
if (c->data == NULL) {
return NULL;
}

} else {
c->data = NULL;
}

c->handler = NULL;
c->next = p->cleanup;

p->cleanup = c;

ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, p->log, 0, "add cleanup: %p", c);

return c;
}



ngx_pool_run_cleanup_file

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

void
ngx_pool_run_cleanup_file(ngx_pool_t *p, ngx_fd_t fd)
{
ngx_pool_cleanup_t *c;
ngx_pool_cleanup_file_t *cf;

for (c = p->cleanup; c; c = c->next) {
if (c->handler == ngx_pool_cleanup_file) {

cf = c->data;

if (cf->fd == fd) {
c->handler(cf);
c->handler = NULL;
return;
}
}
}
}



ngx_pool_cleanup_file

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

void
ngx_pool_cleanup_file(void *data)
{
ngx_pool_cleanup_file_t *c = data;

ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, c->log, 0, "file cleanup: fd:%d",
c->fd);

if (ngx_close_file(c->fd) == NGX_FILE_ERROR) {
ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
ngx_close_file_n " \"%s\" failed", c->name);
}
}



ngx_pool_delete_file

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

void
ngx_pool_delete_file(void *data)
{
ngx_pool_cleanup_file_t *c = data;

ngx_err_t err;

ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, c->log, 0, "file cleanup: fd:%d %s",
c->fd, c->name);

if (ngx_delete_file(c->name) == NGX_FILE_ERROR) {
err = ngx_errno;

if (err != NGX_ENOENT) {
ngx_log_error(NGX_LOG_CRIT, c->log, err,
ngx_delete_file_n " \"%s\" failed", c->name);
}
}

if (ngx_close_file(c->fd) == NGX_FILE_ERROR) {
ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
ngx_close_file_n " \"%s\" failed", c->name);
}
}



遗留问题

ngx_align_ptr和memalign的区别

cleanup函数的使用

ending

young-woman-enjoying-freedom-at-wheat-field-picjumbo-com_lit.jpg

----------- ending -----------