Nginx学习笔记(五) 源码分析内存模块内存对齐
Nginx源碼分析&內存模塊
今天總結了下C語言的內存分配問題,那么就看看Nginx的內存分配相關模型的具體實現。還有內存對齊的內容~~不懂的可以看看~~
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這里部分代碼是關于內存的申請的,是對Linux原有的內存申請函數的再一次封裝。
1.函數聲明:
void *ngx_alloc(size_t size, ngx_log_t *log); //申請空間 void *ngx_calloc(size_t size, ngx_log_t *log); //申請空間,并初始化為02.源碼解析:
void * ngx_alloc(size_t size, ngx_log_t *log) {void *p;p = malloc(size);//malloc就是返回一個void*指針,指向分配的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; }void * ngx_calloc(size_t size, ngx_log_t *log) {void *p;p = ngx_alloc(size, log);//調用上面的函數if (p) {ngx_memzero(p, size);//并初始化為0,#define ngx_memzero(buf, n) (void) memset(buf, 0, n)}
return p;
}
3.POSIX_MEMALIGN與MEMALIGN申請對齊內存,可以參考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數據對齊
概念:
對齊跟數據在內存中的位置有關,為了使CPU能夠對變量進行快速的訪問,變量的起始地址應該具有某些特性,即所謂的”對齊”。 比如4字節的int型,其起始地址應該位于4字節的邊界上,即起始地址能夠被4整除。
功能:
字節對齊的作用不僅是便于cpu快速訪問,同時合理的利用字節對齊可以有效地節省存儲空間。
具體方法:
指定對齊值:#pragma pack (value)時的指定對齊值value。
取消對齊值:#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 ?//默認情況下,1字節的a在0x00000000,而整形b只能放在0x00000004(必須從4的整數倍開始)~0x00000007,最后的c在0x00000008~0x00000009
sizeof(struct B)=8 ? ?//分析同上
sizeof(struct C)=7 ? ?//這里指定了對齊值為1,那么a在0x00000000,b在0x00000001~0x0000004,c在0x00000005~0x00000006
sizeof(struct D)=8 ? ?//分析同上
A、B、C、D的內存地址如圖:
| 地址 | 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內存池分析
上源碼:
/** 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默認的內存池的大小為16*1024。
2.結構體ngx_pool_data_t內存數據塊,ngx_pool_s內存池頭部結構:
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_chain_t結構ngx_pool_large_t *large; //大塊內存鏈表,即分配空間超過max的內存ngx_pool_cleanup_t *cleanup; //釋放內存池的callbackngx_log_t *log; //日志信息 };3.創建和銷毀內存池:
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); //下一次分配的開始地址,sizeof(ngx_pool_t)為申請的P的大小p->d.end = (u_char *) p + size; //內存池結束位置,size是申請空間的小小 p->d.next = NULL; //內存鏈表的指向下一內存塊的指針為空p->d.failed = 0; //失敗次數size = size - sizeof(ngx_pool_t); //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; } //該函數將遍歷內存池鏈表,所有釋放內存,如果注冊了clenup(也是一個鏈表結構),亦將遍歷該cleanup鏈表結構依次調用clenup的handler清理。同時,還將遍歷large鏈表,釋放大塊內存。
void ngx_destroy_pool(ngx_pool_t *pool)//刪除全部內存池(鏈上的所有內存塊) {ngx_pool_t *p, *n;ngx_pool_large_t *l;ngx_pool_cleanup_t *c;//根據注冊的ngx_pool_cleanup_s 來逐個銷毀內存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); }}//銷毀大內存塊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
//普通內存池
for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {ngx_free(p);if (n == NULL) {break;}}
4.重置內存池:
//該函數將釋放所有large內存,并且將d->last指針重新指向ngx_pool_t結構之后數據區的開始位置,同剛創建后的位置相同。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);//專門用于釋放大內存ngx_free()}}//大內存塊置為空pool->large = NULL;//重新修改每個內存塊的大小for (p = pool; p; p = p->d.next) {p->d.last = (u_char *) p + sizeof(ngx_pool_t);} }
5.注冊cleanup
//cleanup結構體 struct ngx_pool_cleanup_s {ngx_pool_cleanup_pt handler;void *data;ngx_pool_cleanup_t *next; };//注冊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));//申請內存池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; }? 6.內存分配函數
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()這個函數:
void * ngx_palloc(ngx_pool_t *pool, size_t size) {u_char *m;ngx_pool_t *p;if (size <= pool->max) {//max與待分配內存進行比較p = pool->current;//從當前位置開始遍歷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大小的內存 }p = p->d.next;} while (p);return ngx_palloc_block(pool, size); //鏈表里沒有能分配size大小內存的節點,則生成一個新的節點并在其中分配內存 }return ngx_palloc_large(pool, size); //大于max值,則在large鏈表里分配內存 }其中的ngx_palloc_block()函數:
//該函數分配一塊內存,并加入到內存池中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); //計算內存池大小 m = ngx_memalign(NGX_POOL_ALIGNMENT, psize, pool->log); //申請與原來相同的大小,這樣的話內存池就是以2的指數冪增大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_pool_data_t結構體之后數據區起始位m = ngx_align_ptr(m, NGX_ALIGNMENT);new->d.last = m + size; //在數據區分配size大小的內存并設置last指針 current = pool->current; for (p = current; p->d.next; p = p->d.next) {if (p->d.failed++ > 4) { //失敗4次以上移動current指針current = p->d.next;}}p->d.next = new; //將這次分配的內存塊new加入該內存池 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
轉載于:https://www.cnblogs.com/coder2012/p/3151346.html
總結
以上是生活随笔為你收集整理的Nginx学习笔记(五) 源码分析内存模块内存对齐的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 高温津贴:关键是消除劳动者“权利贫困”
- 下一篇: 打油诗