内存分配相关
系统功能封装
内存相关的操作主要在 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;
下面是我简单画的一个图来描述这个结构:
下面解释一下主要的几个操作:
- // 创建内存池
- 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
- #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
运行
显示结果- 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
阅读(1736) | 评论(0) | 转发(1) |