Nginx学习笔记(五) 源码分析内存模块内存对齐
Nginx源碼分析&內(nèi)存模塊
今天總結(jié)了下C語言的內(nèi)存分配問題,那么就看看Nginx的內(nèi)存分配相關(guān)模型的具體實(shí)現(xiàn)。還有內(nèi)存對(duì)齊的內(nèi)容~~不懂的可以看看~~
src/os/unix/Ngx_alloc.h&Ngx_alloc.c
先上源碼:
/** Copyright (C) Igor Sysoev* Copyright (C) Nginx, Inc.*/#ifndef _NGX_ALLOC_H_INCLUDED_ #define _NGX_ALLOC_H_INCLUDED_#include <ngx_config.h> #include <ngx_core.h>void *ngx_alloc(size_t size, ngx_log_t *log); void *ngx_calloc(size_t size, ngx_log_t *log);#define ngx_free free/** Linux has memalign() or posix_memalign()* Solaris has memalign()* FreeBSD 7.0 has posix_memalign(), besides, early version's malloc()* aligns allocations bigger than page size at the page boundary*/#if (NGX_HAVE_POSIX_MEMALIGN || NGX_HAVE_MEMALIGN)void *ngx_memalign(size_t alignment, size_t size, ngx_log_t *log);#else#define ngx_memalign(alignment, size, log) ngx_alloc(size, log)#endifextern ngx_uint_t ngx_pagesize; extern ngx_uint_t ngx_pagesize_shift; extern ngx_uint_t ngx_cacheline_size;#endif /* _NGX_ALLOC_H_INCLUDED_ */ View Code這里部分代碼是關(guān)于內(nèi)存的申請(qǐng)的,是對(duì)Linux原有的內(nèi)存申請(qǐng)函數(shù)的再一次封裝。
1.函數(shù)聲明:
void *ngx_alloc(size_t size, ngx_log_t *log); //申請(qǐng)空間 void *ngx_calloc(size_t size, ngx_log_t *log); //申請(qǐng)空間,并初始化為02.源碼解析:
void * ngx_alloc(size_t size, ngx_log_t *log) {void *p;p = malloc(size);//malloc就是返回一個(gè)void*指針,指向分配的size大小的內(nèi)存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; }void * ngx_calloc(size_t size, ngx_log_t *log) {void *p;p = ngx_alloc(size, log);//調(diào)用上面的函數(shù)if (p) {ngx_memzero(p, size);//并初始化為0,#define ngx_memzero(buf, n) (void) memset(buf, 0, n)}
return p;
}
3.POSIX_MEMALIGN與MEMALIGN申請(qǐng)對(duì)齊內(nèi)存,可以參考Linux man page:http://man7.org/linux/man-pages/man3/valloc.3.html
#if (NGX_HAVE_POSIX_MEMALIGN || NGX_HAVE_MEMALIGN)void *ngx_memalign(size_t alignment, size_t size, ngx_log_t *log);#else#define ngx_memalign(alignment, size, log) ngx_alloc(size, log)#endif #if (NGX_HAVE_POSIX_MEMALIGN) void * ngx_memalign(size_t alignment, size_t size, ngx_log_t *log) {void *p;int err;err = posix_memalign(&p, alignment, size);//stdlib.h 新接口if (err) {ngx_log_error(NGX_LOG_EMERG, log, err,"posix_memalign(%uz, %uz) failed", alignment, size);p = NULL;}ngx_log_debug3(NGX_LOG_DEBUG_ALLOC, log, 0,"posix_memalign: %p:%uz @%uz", p, size, alignment);return p; } #elif (NGX_HAVE_MEMALIGN) void * ngx_memalign(size_t alignment, size_t size, ngx_log_t *log) {void *p;p = memalign(alignment, size);//malloc.h 老接口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; } #endif數(shù)據(jù)對(duì)齊
概念:
對(duì)齊跟數(shù)據(jù)在內(nèi)存中的位置有關(guān),為了使CPU能夠?qū)ψ兞窟M(jìn)行快速的訪問,變量的起始地址應(yīng)該具有某些特性,即所謂的”對(duì)齊”。 比如4字節(jié)的int型,其起始地址應(yīng)該位于4字節(jié)的邊界上,即起始地址能夠被4整除。
功能:
字節(jié)對(duì)齊的作用不僅是便于cpu快速訪問,同時(shí)合理的利用字節(jié)對(duì)齊可以有效地節(jié)省存儲(chǔ)空間。
具體方法:
指定對(duì)齊值:#pragma pack (value)時(shí)的指定對(duì)齊值value。
取消對(duì)齊值:#pragma pach ()
具體分析:
struct A{char a; //1int b; //4short c; //2 }struct B{int b;char a;short c; }#pragma pack(1) struct C{char a;int b;short c; } #pragma pack()#pragma pach(2) struct D{char a;int b;short c; } #pragma pack()代碼如上,想一想答案都是多少?
? sizeof(struct A)=10 ?//默認(rèn)情況下,1字節(jié)的a在0x00000000,而整形b只能放在0x00000004(必須從4的整數(shù)倍開始)~0x00000007,最后的c在0x00000008~0x00000009
sizeof(struct B)=8 ? ?//分析同上
sizeof(struct C)=7 ? ?//這里指定了對(duì)齊值為1,那么a在0x00000000,b在0x00000001~0x0000004,c在0x00000005~0x00000006
sizeof(struct D)=8 ? ?//分析同上
A、B、C、D的內(nèi)存地址如圖:
| 地址 | 0x00000000 | 0x01 | 0x02 | 0x03 | 004 | 0x05 | 0x06 | 0x07 | 0x08 | 0x09 |
| A | a | ? | ? | ? | b | c | ||||
| B | b | a | ? | c | ? | ? | ||||
| C | a | b | c | ? | ? | ? | ||||
| D | a | ? | b | c | ? | ? | ||||
?src/core/Ngx_palloc.h&Ngx_palloc.cn內(nèi)存池分析
上源碼:
/** Copyright (C) Igor Sysoev* Copyright (C) Nginx, Inc.*/#ifndef _NGX_PALLOC_H_INCLUDED_ #define _NGX_PALLOC_H_INCLUDED_#include <ngx_config.h> #include <ngx_core.h>/** NGX_MAX_ALLOC_FROM_POOL should be (ngx_pagesize - 1), i.e. 4095 on x86.* On Windows NT it decreases a number of locked pages in a kernel.*/ #define NGX_MAX_ALLOC_FROM_POOL (ngx_pagesize - 1)#define NGX_DEFAULT_POOL_SIZE (16 * 1024)#define NGX_POOL_ALIGNMENT 16 #define NGX_MIN_POOL_SIZE \ngx_align((sizeof(ngx_pool_t) + 2 * sizeof(ngx_pool_large_t)), \NGX_POOL_ALIGNMENT)typedef void (*ngx_pool_cleanup_pt)(void *data);typedef struct ngx_pool_cleanup_s ngx_pool_cleanup_t;struct ngx_pool_cleanup_s {ngx_pool_cleanup_pt handler;void *data;ngx_pool_cleanup_t *next; };typedef struct ngx_pool_large_s ngx_pool_large_t;struct ngx_pool_large_s {ngx_pool_large_t *next;void *alloc; };typedef struct {u_char *last;u_char *end;ngx_pool_t *next;ngx_uint_t failed; } ngx_pool_data_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 {ngx_fd_t fd;u_char *name;ngx_log_t *log; } ngx_pool_cleanup_file_t;void *ngx_alloc(size_t size, ngx_log_t *log); void *ngx_calloc(size_t size, ngx_log_t *log);ngx_pool_t *ngx_create_pool(size_t size, ngx_log_t *log); void ngx_destroy_pool(ngx_pool_t *pool); void ngx_reset_pool(ngx_pool_t *pool);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); ngx_int_t ngx_pfree(ngx_pool_t *pool, void *p);ngx_pool_cleanup_t *ngx_pool_cleanup_add(ngx_pool_t *p, size_t size); void ngx_pool_run_cleanup_file(ngx_pool_t *p, ngx_fd_t fd); void ngx_pool_cleanup_file(void *data); void ngx_pool_delete_file(void *data);#endif /* _NGX_PALLOC_H_INCLUDED_ */ View Code1.#define NGX_DEFAULT_POOL_SIZE ? ?(16 * 1024),表示NGX默認(rèn)的內(nèi)存池的大小為16*1024。
2.結(jié)構(gòu)體ngx_pool_data_t內(nèi)存數(shù)據(jù)塊,ngx_pool_s內(nèi)存池頭部結(jié)構(gòu):
typedef struct {u_char *last; //當(dāng)前內(nèi)存池分配到此處,即下一次分配從此處開始u_char *end; //內(nèi)存池結(jié)束位置ngx_pool_t *next; //內(nèi)存池里面有很多塊內(nèi)存,這些內(nèi)存塊就是通過該指針連成鏈表的ngx_uint_t failed; //內(nèi)存池分配失敗次數(shù)} ngx_pool_data_t; //內(nèi)存池的數(shù)據(jù)塊位置信息struct ngx_pool_s{ //內(nèi)存池頭部結(jié)構(gòu)ngx_pool_data_t d; //內(nèi)存池的數(shù)據(jù)塊size_t max; //內(nèi)存池?cái)?shù)據(jù)塊的最大值ngx_pool_t *current; //指向當(dāng)前內(nèi)存池ngx_chain_t *chain; //該指針掛接一個(gè)ngx_chain_t結(jié)構(gòu)ngx_pool_large_t *large; //大塊內(nèi)存鏈表,即分配空間超過max的內(nèi)存ngx_pool_cleanup_t *cleanup; //釋放內(nèi)存池的callbackngx_log_t *log; //日志信息 };3.創(chuàng)建和銷毀內(nèi)存池:
ngx_pool_t * ngx_create_pool(size_t size, ngx_log_t *log)//創(chuàng)建內(nèi)存池{ngx_pool_t *p;p = ngx_memalign(NGX_POOL_ALIGNMENT, size, log); //申請(qǐng)對(duì)齊內(nèi)存空間if (p == NULL) {return NULL;}p->d.last = (u_char *) p + sizeof(ngx_pool_t); //下一次分配的開始地址,sizeof(ngx_pool_t)為申請(qǐng)的P的大小p->d.end = (u_char *) p + size; //內(nèi)存池結(jié)束位置,size是申請(qǐng)空間的小小 p->d.next = NULL; //內(nèi)存鏈表的指向下一內(nèi)存塊的指針為空p->d.failed = 0; //失敗次數(shù)size = size - sizeof(ngx_pool_t); //p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL;//內(nèi)存池最大塊 p->current = p; //當(dāng)前指向的內(nèi)存塊p->chain = NULL;p->large = NULL;p->cleanup = NULL;p->log = log;return p; } //該函數(shù)將遍歷內(nèi)存池鏈表,所有釋放內(nèi)存,如果注冊(cè)了clenup(也是一個(gè)鏈表結(jié)構(gòu)),亦將遍歷該cleanup鏈表結(jié)構(gòu)依次調(diào)用clenup的handler清理。同時(shí),還將遍歷large鏈表,釋放大塊內(nèi)存。
void ngx_destroy_pool(ngx_pool_t *pool)//刪除全部?jī)?nèi)存池(鏈上的所有內(nèi)存塊) {ngx_pool_t *p, *n;ngx_pool_large_t *l;ngx_pool_cleanup_t *c;//根據(jù)注冊(cè)的ngx_pool_cleanup_s 來逐個(gè)銷毀內(nèi)存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); }}//銷毀大內(nèi)存塊for (l = pool->large; l; l = l->next) {ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0, "free: %p", l->alloc);if (l->alloc) {ngx_free(l->alloc);}} #if (NGX_DEBUG)/** 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
//普通內(nèi)存池
for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {ngx_free(p);if (n == NULL) {break;}}
4.重置內(nèi)存池:
//該函數(shù)將釋放所有large內(nèi)存,并且將d->last指針重新指向ngx_pool_t結(jié)構(gòu)之后數(shù)據(jù)區(qū)的開始位置,同剛創(chuàng)建后的位置相同。void ngx_reset_pool(ngx_pool_t *pool) {ngx_pool_t *p;ngx_pool_large_t *l;//刪除大內(nèi)存塊for (l = pool->large; l; l = l->next) {if (l->alloc) {ngx_free(l->alloc);//專門用于釋放大內(nèi)存ngx_free()}}//大內(nèi)存塊置為空pool->large = NULL;//重新修改每個(gè)內(nèi)存塊的大小for (p = pool; p; p = p->d.next) {p->d.last = (u_char *) p + sizeof(ngx_pool_t);} }
5.注冊(cè)cleanup
//cleanup結(jié)構(gòu)體 struct ngx_pool_cleanup_s {ngx_pool_cleanup_pt handler;void *data;ngx_pool_cleanup_t *next; };//注冊(cè)cleanup函數(shù),為以后清除做準(zhǔn)備 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));//申請(qǐng)內(nèi)存池if (c == NULL) {return NULL;}if (size) {c->data = ngx_palloc(p, size); //申請(qǐng)數(shù)據(jù)空間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; }? 6.內(nèi)存分配函數(shù)
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);主要介紹一下ngx_palloc()這個(gè)函數(shù):
void * ngx_palloc(ngx_pool_t *pool, size_t size) {u_char *m;ngx_pool_t *p;if (size <= pool->max) {//max與待分配內(nèi)存進(jìn)行比較p = pool->current;//從當(dāng)前位置開始遍歷pool鏈表do {m = ngx_align_ptr(p->d.last, NGX_ALIGNMENT);if ((size_t) (p->d.end - m) >= size) {p->d.last = m + size;return m; //成功分配size大小的內(nèi)存 }p = p->d.next;} while (p);return ngx_palloc_block(pool, size); //鏈表里沒有能分配size大小內(nèi)存的節(jié)點(diǎn),則生成一個(gè)新的節(jié)點(diǎn)并在其中分配內(nèi)存 }return ngx_palloc_large(pool, size); //大于max值,則在large鏈表里分配內(nèi)存 }其中的ngx_palloc_block()函數(shù):
//該函數(shù)分配一塊內(nèi)存,并加入到內(nèi)存池中static void * ngx_palloc_block(ngx_pool_t *pool, size_t size) {u_char *m;size_t psize;ngx_pool_t *p, *new, *current;psize = (size_t) (pool->d.end - (u_char *) pool); //計(jì)算內(nèi)存池大小 m = ngx_memalign(NGX_POOL_ALIGNMENT, psize, pool->log); //申請(qǐng)與原來相同的大小,這樣的話內(nèi)存池就是以2的指數(shù)冪增大if (m == NULL) {return NULL;}new = (ngx_pool_t *) m; //新的內(nèi)存塊new->d.end = m + psize;new->d.next = NULL;new->d.failed = 0;m += sizeof(ngx_pool_data_t);//讓m指向該塊內(nèi)存ngx_pool_data_t結(jié)構(gòu)體之后數(shù)據(jù)區(qū)起始位m = ngx_align_ptr(m, NGX_ALIGNMENT);new->d.last = m + size; //在數(shù)據(jù)區(qū)分配size大小的內(nèi)存并設(shè)置last指針 current = pool->current; for (p = current; p->d.next; p = p->d.next) {if (p->d.failed++ > 4) { //失敗4次以上移動(dòng)current指針current = p->d.next;}}p->d.next = new; //將這次分配的內(nèi)存塊new加入該內(nèi)存池 pool->current = current ? current : new;return m; }
參考
http://hi.baidu.com/langwan/item/fdd3bf4a4ef66aefa4c06629
http://blog.csdn.net/wallwind/article/details/7463979
http://blog.csdn.net/livelylittlefish/article/details/6586946
轉(zhuǎn)載于:https://www.cnblogs.com/coder2012/p/3151346.html
總結(jié)
以上是生活随笔為你收集整理的Nginx学习笔记(五) 源码分析内存模块内存对齐的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 高温津贴:关键是消除劳动者“权利贫困”
- 下一篇: 打油诗