内存分配相关
系统功能封装
内存相关的操作主要在 os/unix/ngx_alloc.{h,c} 和 core/ngx_palloc.{h,c} 下
其中 os/unix/ngx_alloc.{h,c} 封装了最基本的内存分配函数,是对c原有的malloc/free/memalign 等原有的函数的封装,对应的函数为:
- ngx_alloc 使用malloc分配内存空间
- ngx_calloc 使用malloc分配内存空间,并且将空间内容初始化为0
- ngx_memalign 返回基于一个指定的alignment大小的数值为对齐基数的空间
- ngx_free 对内存的释放操作
ngx的内存池
为了方便系统模块对内存的使用,方便内存的管理,nginx自己实现了进程池的机制来进行内存的分配和释放, 首先nginx会在特定的生命周期帮你统一建立内存池,当需要进行内存分配的时候统一通过内存池中的内存进行分配,最后nginx会在适当的时候释放内存池的资源,开发者只要在需要的时候对内存进行申请即可,不用过多考虑内存的释放等问题,大大提高了开发的效率。
内存池的主要结构为:
//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 ; }; //ngx_core.h typedef struct ngx_pool_s ngx_pool_t ; typedef struct ngx_chain_s ngx_chain_t ;
下面是我简单画的一个图来描述这个结构:
link : http://www.flickr.com/photos/rainx/3765612584/sizes/o/
下面解释一下主要的几个操作:
// 创建内存池 ngx_pool_t * ngx_create_pool ( size_t size , ngx_log_t * log );
大致的过程是创建使用 ngx_alloc 分配一个size大小的空间, 然后将 ngx_pool_t* 指向这个空间, 并且初始化里面的成员, 其中
p -> d . last = ( u_char *) p + sizeof ( ngx_pool_t ); // 初始指向 ngx_pool_t 结构体后面 p -> d . end = ( u_char *) p + size ; // 整个结构的结尾后面 p -> max = ( size < NGX_MAX_ALLOC_FROM_POOL ) ? size : NGX_MAX_ALLOC_FROM_POOL ; // 最大不超过 NGX_MAX_ALLOC_FROM_POOL,也就是getpagesize()-1 大小
其他大都设置为null或者0
// 销毁内存池 void ngx_destroy_pool ( ngx_pool_t * pool );
遍历链表,所有释放内存,其中如果注册了clenup(也是一个链表结构), 会一次调用clenup 的 handler 进行清理。
// 重置内存池 void ngx_reset_pool ( ngx_pool_t * pool );
释放所有large段内存, 并且将d->last指针重新指向 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_pcalloc ( ngx_pool_t * pool , size_t size ); void * ngx_pmemalign ( ngx_pool_t * pool , size_t size , size_t alignment );
ngx_palloc的过程一般为,首先判断待分配的内存是否大于 pool->max的大小,如果大于则使用 ngx_palloc_large 在 large 链表里分配一段内存并返回, 如果小于测尝试从链表的 pool->current 开始遍历链表,尝试找出一个可以分配的内存,当链表里的任何一个节点都无法分配内存的时候,就调用 ngx_palloc_block 生成链表里一个新的节点, 并在新的节点里分配内存并返回, 同时, 还会将pool->current 指针指向新的位置(从链表里面pool->d.failed小于等于4的节点里找出) ,其他几个函数也基本上为 ngx_palloc 的变种,实现方式大同小异
// 释放指定的内存 ngx_int_t ngx_pfree ( ngx_pool_t * pool , void * p );
这个操作只有在内存在large链表里注册的内存在会被真正释放,如果分配的是普通的内存,则会在destory_pool的时候统一释放.
// 注册cleanup回叫函数(结构体) ngx_pool_cleanup_t * ngx_pool_cleanup_add ( ngx_pool_t * p , size_t size );
这个过程和我们之前经常使用的有些区别, 他首先在传入的内存池中分配这个结构的空间(包括data段), 然后将为结构体分配的空间返回, 通过操作返回的ngx_pool_cleanup_t结构来添加回叫的实现。 ( 这个过程在nginx里面出现的比较多,也就是 xxxx_add 操作通常不是实际的添加操作,而是分配空间并返回一个指针,后续我们还要通过操作指针指向的空间来实现所谓的add )
下面是内存操作的一些例子 demo/basic_types/mem_op.c
#include <stdio.h> #include "ngx_config.h" #include "ngx_conf_file.h" #include "nginx.h" #include "ngx_core.h" #include "ngx_string.h" #include "ngx_palloc.h" volatile 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 , ...) { } typedef struct example_s { int a ; char * b ; } example_t ; int main () { ngx_pool_t * pool ; example_t * exp ; char * s ; pool = ngx_create_pool ( 5000 , NULL ); printf ( "available pool regular pool free size is %d now\n" , ( ngx_uint_t ) ( pool -> d . end - pool -> d . last )); exp = ngx_palloc ( pool , sizeof ( example_t )) ; s = ngx_palloc ( pool , sizeof ( "hello,world" )); printf ( "available pool regular pool free size is %d now\n" , ( ngx_uint_t ) ( pool -> d . end - pool -> d . last )); exp -> a = 1 ; exp -> b = s ; strcpy ( s , "hello,world" ); printf ( "pool max is %d\n" , pool -> max ); printf ( "exp->a is %d, exp->b is %s\n" , exp -> a , exp -> b ); ngx_destroy_pool ( pool ); return 0 ; }
编译运行结果
gcc - c - O - pipe - O - W - Wall - Wpointer - arith - Wno - unused - parameter - Wunused - function - Wunused - variable - Wunused - value - Werror - g - I ../../../ objs / - I ../../ os / unix / mem_op . c - I ../../ core / - I ../../ event / - I ../../ os / - o mem_op . o gcc - o mem_op mem_op . o ../../../ objs / src / core / ngx_ { string , palloc }. o ../../../ objs / src / os / unix / ngx_alloc . o - lcrypt - lpcre - lcrypto - lz rainx@rainx - laptop :~/ land / nginx - 0.7 . 61 / src / demo / basic_types$ ./ mem_op available pool regular pool free size is 4960 now available pool regular pool free size is 4940 now pool max is 4960 exp -> a is 1 , exp -> b is hello , world