內存分配相關
1. 系統功能封裝
內存相關的操作主要在os/unix/ngx_alloc.{h,c} 和 core/ngx_palloc.{h,c}中。
其中os/unix/ngx_alloc.{h,c}封裝了最基本的內存分配函數,是對c原有的malloc/free/memalign等函數的封裝,對應函數爲:
a.ngx_alloc:對malloc進行了簡單的封裝;
void *
ngx_alloc(size_t size, ngx_log_t *log)
{
void *p;
p = malloc(size);
if (p == NULL) {
ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
"malloc(%uz) failed", size); }
ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, log, 0, "malloc: %p:%uz", p, size);
return p;
}
b.ngx_calloc:使用ngx_alloc分配內存,並且把內存賦值0:
void *
ngx_calloc(size_t size, ngx_log_t *log)
{
void *p;
p = ngx_alloc(size, log);
if (p) { ngx_memzero(p, size); }//在core/ngx_string.h中定義
// #define ngx_memzero(buf, n) (void) memset(buf, 0, n) 初始化爲0
return p;
}
c. ngx_memalign 返回基於一個指定的alignment大小的數值爲對齊基數的空間
d.ngx_free 內存釋放操作
2. nginx內存池
爲了方便系統模塊對內存的使用,方便內存的管理,nginx自己施行了進程池機制來進行內存的分配和釋放,首先nginx會在特定的生命週期幫你統一建立內存池,當需要進行內存分配的時候同一通過內存池中的內存進行分配,最後nginx會在適當的時候釋放內存吃的資源,開發者只要在需要的時候對內存進行申請即可,不用過多考慮釋放的問題,這也就是在os/unix/ngx_alloc.c文件中沒有看到free操作的原因吧。
下面來看一下內存池的主要結構:
<span style="font-size:16px;">ngx_palloc.h
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;
ngx_core.h
typedef struct ngx_pool_s ngx_pool_t;
typedef struct ngx_chain_s ngx_chain_t;
</span>
<span style="font-size:16px;">src/core/ngx_palloc.c
//創建內存池
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); //初始指向ngx_pool_t結構體後面
p->d.end = (u_char *) p + size; //整個結構體的結尾
p->d.next = NULL; //沒有next
p->d.failed = 0;
size = size - sizeof(ngx_pool_t); //剩餘大小
p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL;//最大不超過NGX_MAX_ALLOC_FROM_POOL
//#define NGX_MAX_ALLOC_FROM_POOL (ngx_pagesize - 1)
p->current = p;
p->chain = NULL;
p->large = NULL;
p->cleanup = NULL;
p->log = log;
return p;
}
//銷燬內存池
void
ngx_destroy_pool(ngx_pool_t *pool)
{
ngx_pool_t *p, *n;
ngx_pool_large_t *l;
ngx_pool_cleanup_t *c;
for (c = pool->cleanup; c; c = c->next) {//如果註冊了clenup(一種鏈表結構),會依次調用clenup的handler進行清理
if (c->handler) {
ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0,
"run cleanup: %p", c);
c->handler(c->data);
}
}
for (l = pool->large; l; l = l->next) { //遍歷鏈表,釋放所有large內存
ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0, "free: %p", l->alloc);
if (l->alloc) {
ngx_free(l->alloc);
}
}
#if (NGX_DEBUG) //等譯debug級別,如果爲true,會打印日誌
/*
* we could allocate the pool->log from this pool
* so we cannot use this log while free()ing the pool
*/
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 (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {//遍歷鏈表 ,釋放內存空間
ngx_free(p);
if (n == NULL) {
break;
}
}
}
//重置內存池
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) { //釋放掉所有large段內存
if (l->alloc) {
ngx_free(l->alloc);
}
}
pool->large = NULL;
for (p = pool; p; p = p->d.next) {
p->d.last = (u_char *) p + sizeof(ngx_pool_t);將指針重新指向ngx_pool_t(和創建時一樣)
}
}
//從內存池裏分配內存
void * ngx_palloc(ngx_pool_t *pool, size_t size)
void * ngx_pnalloc(ngx_pool_t *pool, size_t size)
void * ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment)
void * ngx_pcalloc(ngx_pool_t *pool, size_t size)
這裏以ngx_palloc爲例講解,其他大同小異:
void *
ngx_palloc(ngx_pool_t *pool, size_t size)
{
u_char *m;
ngx_pool_t *p;
if (size <= pool->max) { //判斷分配內存是否大於pool->max,如果小於等於
p = pool->current; //嘗試從鏈表的current開始遍歷,
do {
m = ngx_align_ptr(p->d.last, NGX_ALIGNMENT);
//#define ngx_align_ptr(p,a)
//(u_char *) (((uintptr_t) (p) + ((uintptr_t) a - 1)) & ~((uintptr_t) a - 1))
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);//如果無法分配內存,就生成一個新的節點,同時pool->current指針指向新的位置
}
return ngx_palloc_large(pool, size); //如果分配的內存大於pool->max則在large鏈表分配一段內存
}
//釋放指定的內存
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) { //存在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;
}//由代碼可以看出,這個操作只有在內存large鏈表裏面註冊內存纔會真正釋放,如果分配的是普通的內存,則會在destory_pool的時候統一釋放。
//註冊cleanup回調函數
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)); //分配cleanup空間
if (c == NULL) {
return NULL;
}
if (size) {
c->data = ngx_palloc(p, size); //爲cleanup結構體分配data空間
if (c->data == NULL) {
return NULL;
}
} else {
c->data = NULL;
}
c->handler = NULL;
c->next = p->cleanup;
p->cleanup = c; // 增加cleanup
ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, p->log, 0, "add cleanup: %p", c);
return c; //返回結構體分配的空間
}</span>