nginx内存池详解_免费下载.docx_第1页
nginx内存池详解_免费下载.docx_第2页
nginx内存池详解_免费下载.docx_第3页
nginx内存池详解_免费下载.docx_第4页
nginx内存池详解_免费下载.docx_第5页
已阅读5页,还剩12页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

Content0.序1.内存池结构1.1 ngx_pool_t结构1.2其他相关结构1.3 ngx_pool_t的逻辑结构2.内存池操作2.1创建内存池2.2销毁内存池2.3重置内存池2.4分配内存2.4.1 ngx_palloc()函数分析2.4.2 ngx_palloc_block()函数分析2.5释放内存2.6注册cleanup2.7内存池的物理结构3.一个例子3.1代码3.2如何编译3.3运行结果4.小结5.致谢0.序nginx对内存的管理由其自己实现的内存池结构ngx_pool_t来完成,本文重点叙述nginx的内存管理。nginx内存管理相关文件:(1) ./src/os/unix/ngx_alloc.h/.c 内存相关的操作,封装了最基本的内存分配函数 如free/malloc/memalign/posix_memalign,分别被封装为ngx_free,ngx_alloc/ngx_calloc, ngx_memalign ngx_alloc:封装malloc分配内存 ngx_calloc:封装malloc分配内存,并初始化空间内容为0 ngx_memalign:返回基于一个指定alignment的大小为size的内存空间,且其地址为alignment的整数倍,alignment为2的幂。(2) ./src/core/ngx_palloc.h/.c 封装创建/销毁内存池,从内存池分配空间等函数.表示nginx-1.0.4代码目录,本文为/usr/src/nginx-1.0.4。1.内存池结构nginx对内存的管理均统一完成,例如,在特定的生命周期统一建立内存池(如main函数系统启动初期即分配1024B大小的内存池),需要内存时统一分配内存池中的内存,在适当的时候释放内存池的内存(如关闭http链接时调用ngx_destroy_pool进行销毁)。因此,开发者只需在需要内存时进行申请即可,不用过多考虑内存的释放等问题,大大提高了开发的效率。先看一下内存池结构。1.1 ngx_pool_t结构此处统一一下概念,内存池的数据块:即分配内存在这些数据块中进行,一个内存池可以有多一个内存池数据块。nginx的内存池结构如下。00048: typedef struct 00049: u_char *last; /当前内存池分配到此处,即下一次分配从此处开始00050: u_char *end; /内存池结束位置00051: ngx_pool_t *next; /内存池里面有很多块内存,这些内存块就是通过该指针连成链表的00052: ngx_uint_t failed; /内存池分配失败次数00053: ngx_pool_data_t; /内存池的数据块位置信息00054:00055:00056: struct ngx_pool_s /内存池头部结构00057: ngx_pool_data_t d; /内存池的数据块00058: size_t max; /内存池数据块的最大值00059: ngx_pool_t *current; /指向当前内存池00060: ngx_chain_t *chain; /该指针挂接一个ngx_chain_t结构00061: ngx_pool_large_t *large; /大块内存链表,即分配空间超过max的内存00062: ngx_pool_cleanup_t *cleanup; /释放内存池的callback00063: ngx_log_t *log; /日志信息00064: ;其中,sizeof(ngx_pool_data_t)=16B,sizeof(ngx_pool_t)=40B。nginx将几乎所有的结构体放在ngx_core.h文件中重新进行了申明,如下。typedef struct ngx_module_s ngx_module_t;typedef struct ngx_conf_s ngx_conf_t;typedef struct ngx_cycle_s ngx_cycle_t;typedef struct ngx_pool_s ngx_pool_t;typedef struct ngx_chain_s ngx_chain_t;typedef struct ngx_log_s ngx_log_t;typedef struct ngx_array_s ngx_array_t;typedef struct ngx_open_file_s ngx_open_file_t;typedef struct ngx_command_s ngx_command_t;typedef struct ngx_file_s ngx_file_t;typedef struct ngx_event_s ngx_event_t;typedef struct ngx_event_aio_s ngx_event_aio_t;typedef struct ngx_connection_s ngx_connection_t;1.2其他相关结构其他与内存池相干的数据结构,如清除资源的cleanup链表,分配的大块内存链表等,如下。00015: /*00016: * NGX_MAX_ALLOC_FROM_POOL should be (ngx_pagesize - 1), i.e. 4095 on x86.00017: * On Windows NT it decreases a number of locked pages in a kernel.00018: */00019: #define NGX_MAX_ALLOC_FROM_POOL (ngx_pagesize - 1) /在x86体系结构下,该值一般为4096B,即4K00020:00021: #define NGX_DEFAULT_POOL_SIZE (16* 1024)00022:00023: #define NGX_POOL_ALIGNMENT 1600024: #define NGX_MIN_POOL_SIZE 00025: ngx_align(sizeof(ngx_pool_t) + 2 * sizeof(ngx_pool_large_t), 00026: NGX_POOL_ALIGNMENT)00027:00028:00029: typedef void (*ngx_pool_cleanup_pt)(void *data); /cleanup的callback类型00030:00031: typedef struct ngx_pool_cleanup_s ngx_pool_cleanup_t;00032:00033: struct ngx_pool_cleanup_s00034: ngx_pool_cleanup_pt handler;00035: void *data; /指向要清除的数据00036: ngx_pool_cleanup_t *next; /下一个cleanup callback00037: ;00038:00039:00040: typedef struct ngx_pool_large_s ngx_pool_large_t;00041:00042: struct ngx_pool_large_s00043: ngx_pool_large_t *next; /指向下一块大块内存00044: void *alloc; /指向分配的大块内存00045: ;.00067: typedef struct 00068: ngx_fd_t fd;00069: u_char *name;00070: ngx_log_t *log;00071: ngx_pool_cleanup_file_t;00072:(gdb) p getpagesize()$18 = 4096全局变量ngx_pagesize的初始化是在如下函数中完成的。./src/os/unix/ngx_posix_gx_int_tngx_os_init(ngx_log_t *log) ngx_uint_t n;#if (NGX_HAVE_OS_SPECIFIC_INIT) if (ngx_os_specific_init(log) != NGX_OK) return NGX_ERROR; #endif ngx_init_setproctitle(log); /* 该函数为glibc的库函数,由系统调用实现,返回内核中的PAGE_SIZE,该值依赖体系结构*/ ngx_pagesize = getpagesize(); ngx_cacheline_size = NGX_CPU_CACHE_LINE; .这些数据结构之间的关系,请参考后面的图。1.3 ngx_pool_t的逻辑结构这些数据结构逻辑结构图如下。注:本文采用UML的方式画出该图。2.内存池操作2.1创建内存池创建内存池有ngx_create_pool()函数完成,代码如下。00015: ngx_pool_t *00016: ngx_create_pool(size_t size, ngx_log_t *log)00017: 00018: ngx_pool_t *p;00019:00020: p = ngx_memalign(NGX_POOL_ALIGNMENT, size, log);00021: if (p = NULL) 00022: return NULL;00023: 00024:00025: p-d.last = (u_char *) p + sizeof(ngx_pool_t); /last指向ngx_pool_t结构体之后数据取起始位置00026: p-d.end = (u_char *) p + size; /end指向分配的整个size大小的内存的末尾00027: p-d.next = NULL;00028: p-d.failed = 0;00029:00030: size = size - sizeof(ngx_pool_t);00031: p-max = (size current = p;00034: p-chain = NULL;00035: p-large = NULL;00036: p-cleanup = NULL;00037: p-log = log;00038:00039: return p;00040: 例如,调用ngx_create_pool(1024, 0x80d1c4c)后,创建的内存池物理结构如下图。2.2销毁内存池销毁内存池由如下函数完成。void ngx_destroy_pool(ngx_pool_t *pool)该函数将遍历内存池链表,所有释放内存,如果注册了clenup(也是一个链表结构),亦将遍历该cleanup链表结构依次调用clenup的handler清理。同时,还将遍历large链表,释放大块内存。2.3重置内存池重置内存池由下面的函数完成。void ngx_reset_pool(ngx_pool_t *pool);该函数将释放所有large内存,并且将d-last指针重新指向ngx_pool_t结构之后数据区的开始位置,同刚创建后的位置相同。2.4分配内存内存分配的函数如下。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);返回值为分配的内存起始地址。选择其中的两个函数进行分析,其他的也很好理解,省略。2.4.1 ngx_palloc()函数分析ngx_palloc()代码如下,分析请参考笔者所加的注释。00115: void *00116: ngx_palloc(ngx_pool_t *pool, size_t size)00117: 00118: u_char *m;00119: ngx_pool_t *p;00120:00121: if (size max) /判断待分配内存与max值00122:00123: p = pool-current; /小于max值,则从current节点开始遍历pool链表00124:00125: do 00126: m = ngx_align_ptr(p-d.last, NGX_ALIGNMENT);/指针对齐00127:00128: if (size_t) (p-d.end - m) = size) 00129: p-d.last = m + size; /在该节点指向的内存块中分配size大小的内存00130:00131: return m;00132: 00133:00134: p = p-d.next;/遍历到下一个数据块00135:00136: while (p);00137:00138: return ngx_palloc_block(pool, size); /链表里没有能分配size大小内存的节点,则生成一个新的节点并在其中分配内存00139: 00140:00141: return ngx_palloc_large(pool, size); /大于max值,则在large链表里分配内存00142: 关于ngx_align_ptr宏:#define ngx_align_ptr(p, a) (u_char *) (uintptr_t) (p) + (uintptr_t) a - 1) & (uintptr_t) a - 1)例如,在2.1节中创建的内存池中分配200B的内存,调用ngx_palloc(pool, 200)后,该内存池物理结构如下图。2.4.2 ngx_palloc_block()函数分析ngx_palloc_block函数代码如下,分析请参考笔者所加的注释。00175: static void *00176: ngx_palloc_block(ngx_pool_t *pool, size_t size)00177: 00178: u_char *m;00179: size_t psize;00180: ngx_pool_t *p, *new, *current;00181:00182: psize = (size_t) (pool-d.end - (u_char *) pool); /计算pool的大小00183:00184: m = ngx_memalign(NGX_POOL_ALIGNMENT, psize, pool-log);/分配一块与pool大小相同的内存00185: if (m = NULL) 00186: return NULL;00187: 00188:00189: new = (ngx_pool_t *) m;00190:00191: new-d.end = m + psize; /设置end指针00192: new-d.next = NULL;00193: new-d.failed = 0;00194:00195: m += sizeof(ngx_pool_data_t); /让m指向该块内存ngx_pool_data_t结构体之后数据区起始位置00196: m = ngx_align_ptr(m, NGX_ALIGNMENT); /按4字节对齐00197: new-d.last = m + size; /在数据区分配size大小的内存并设置last指针00198:00199: current = pool-current;00200:00201: for (p = current; p-d.next; p = p-d.next) 00202: if (p-d.failed+ 4) /failed的值只在此处被修改00203: current = p-d.next; /失败4次以上移动current指针(失败次数大于5时,不再使用该内存块)00204: 00205: 00206:00207: p-d.next = new; /将这次分配的内存块new加入该内存池00208:00209: pool-current = current ? current : new;00210:00211: return m;00212: 注意:该函数分配一块内存后,last指针指向的是ngx_pool_data_t结构体(大小16B)之后数据区的起始位置。而创建内存池时时,last指针指向的是ngx_pool_t结构体(大小40B)之后数据区的起始位置。结合2.7节的内存池的物理结构,更容易理解。2.5释放内存请参考如下函数,不再赘述。ngx_int_tngx_pfree(ngx_pool_t *pool, void *p)需要注意的是该函数只释放large链表中注册的内存,普通内存在ngx_destroy_pool中统一释放。2.6注册cleanup请参考如下函数,该函数实现也很简单,此处不再赘述。ngx_pool_cleanup_t *ngx_pool_cleanup_add(ngx_pool_t *p, size_t size)2.7内存池的物理结构针对本文第3节的例子,画出的内存池的物理结构如下图。从该图也能看出2.4节的结论,即内存池第一块内存前40字节为ngx_pool_t结构,后续加入的内存块前16个字节为ngx_pool_data_t结构,这两个结构之后便是真正可以分配内存区域。因此,本文Reference中的内存分配相关中的图是有一点点小问题的,并不是每一个节点的前面都是ngx_pool_t结构。3.一个例子理解并掌握开源软件的最好方式莫过于自己写一些测试代码,或者改写软件本身,并进行调试来进一步理解开源软件的原理和设计方法。本节给出一个创建内存池并从中分配内存的简单例子。3.1代码/* * ngx_pool_t test, to test ngx_palloc, ngx_palloc_block, ngx_palloc_large */#include #include ngx_config.h#include ngx_conf_file.h#include nginx.h#include ngx_core.h#include ngx_string.h#include ngx_palloc.hvolatile ngx_cycle_t *ngx_cycle;void ngx_log_error_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err, const char *fmt, .)void dump_pool(ngx_pool_t* pool) while (pool) printf(pool = 0x%xn, pool); printf( .dn); printf( .last = 0x%xn, pool-d.last); printf( .end = 0x%xn, pool-d.end); printf( .next = 0x%xn, pool-d.next); printf( .failed = %dn, pool-d.failed); printf( .max = %dn, pool-max); printf( .current = 0x%xn, pool-current); printf( .chain = 0x%xn, pool-chain); printf( .large = 0x%xn, pool-large); printf( .cleanup = 0x%xn, pool-cleanup); printf( .log = 0x%xn, pool-log); printf(available pool memory = %dnn, pool-d.end - pool-d.last); pool = pool-d.next; int main() ngx_pool_t *pool; printf(-n); printf(create a new pool:n); printf(-n); pool = ngx_create_pool(1024, NULL); dump_pool(pool); printf(-n); printf(alloc block 1 from the pool:n); printf(-n); ngx_palloc(pool, 512); dump_pool(pool); printf(-n); printf(alloc block 2 from the pool:n); printf(-n); ngx_palloc(pool, 512); dump_pool(pool); printf(-n); printf(alloc block 3 from the pool :n); printf(-n); ngx_palloc(pool, 512); dump_pool(pool); ngx_destroy_pool(pool); return 0;3.2如何编译这个问题是编写测试代码或者改写软件本身最迫切需要解决的问题,否则,编写的代码无从编译或运行,那也无从进行调试并理解软件了。如何对自己编写的测试代码进行编译,可参考Linux平台代码覆盖率测试-编译过程自动化及对链接的解释、Linux平台如何编译使用Google test写的单元测试?。我们要做的是学习这种编译工程的方法,针对该例子,笔者编写的makefile文件如下。这便是本节的主要目的。CXX = gccCXXFLAGS += -g -Wall -WextraNGX_ROOT = /usr/src/nginx-1.0.4TARGETS = ngx_pool_t_testTARGETS_C_FILE = $(TARGETS).cCLEANUP = rm -f $(TARGETS) *.oall: $(TARGETS)clean:$(CLEANUP)CORE_INCS = -I. -I$(NGX_ROOT)/src/core -I$(NGX_ROOT)/src/event -I$(NGX_ROOT)/src/event/modules -I$(NGX_ROOT)/src/os/unix -I$(NGX_ROOT)/objs NGX_PALLOC = $(NGX_ROOT)/objs/src/core/ngx_palloc.oNGX_STRING = $(NGX_ROOT)/objs/src/core/ngx_string.oNGX_ALLOC = $(NGX_ROOT)/objs/src/os/unix/ngx_alloc.o$(TARGETS): $(TARGETS_C_FILE)$(CXX) $(CXXFLAGS) $(CORE_INCS) $(NGX_PALLOC) $(NGX_STRING) $(NGX_ALLOC) $ -o $3.3运行运行结果# ./ngx_pool_t_test-create a new pool:-pool = 0x8922020 .d .last = 0x8922048 .end = 0x8922420 .next = 0x0 .failed = 0 .max = 984 .current = 0x8922020 .chain = 0x0 .large = 0x0 .cleanup = 0x0 .log = 0x0available pool memory = 984-alloc block 1 from the pool:-pool = 0x8922020 .d .last = 0x8922248 .end = 0x8922420 .next = 0x0 .failed = 0 .max = 984 .current = 0x8922020 .chain = 0x0 .large = 0x0 .cleanup = 0x0 .log = 0x0available pool memory = 472-alloc block 2 from the pool:-pool = 0x8922020 .d .last = 0x8922248 .end = 0x8922420 .next = 0x8922450 .failed = 0 .max = 984 .current = 0x8922020 .chain = 0x0 .large = 0x0 .cleanup = 0x0 .log = 0x0available pool memory = 472pool = 0x8922450 .d .last = 0x8922660 .end = 0x8922850 .next = 0x0 .failed = 0 .max = 0 .current = 0x0 .chain = 0x0 .large = 0x0 .cleanup = 0x0 .log = 0x0available pool memory = 496-alloc block 3 from the pool :-pool = 0x8922020 .d .last = 0x8922248 .end = 0x8922420 .next = 0x8922450 .failed = 1 .max = 984 .current = 0x8922020 .chain = 0x0 .large = 0x0 .cleanup = 0x0 .log = 0x0available pool memory = 4

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论