nginx坑记录
2019-09-12 20:49 itwolf 阅读(501) 评论(0) 编辑 收藏 举报问题1: 配置解析过程使用ngx_cycle->pool申请内存保存配置,结果造成野指针。
背景:需求开发过程,有一些结构需要在配置解析阶段保存,然后可以动态修改。看原来的代码配置解析都是使用cf->pool进行内存申请,但动态修改的过程显然拿不到cf,于是想到了一个全局的内存池ngx_cycle->pool,想当然的认为ngx_cycle的生命周期是从配置解析开始到进程退出,期间是不会释放ngx_cycle->pool的,但其实是错误的,具体原因如下:
nginx的启动源码简化后如下所示,从代码中的注释可以看出原来配置解析前后ngx_cycle变量中的pool不是同一个:
int ngx_cdecl main(int argc, char *const *argv) { ngx_cycle_t *cycle, init_cycle; ... ngx_memzero(&init_cycle, sizeof(ngx_cycle_t)); init_cycle.log = log; ngx_cycle = &init_cycle; ... //配置解析前创建的pool,解析过程拿到的ngx_cycle->pool就是这个 init_cycle.pool = ngx_create_pool(1024, log); if (init_cycle.pool == NULL) { return 1; } ... cycle = ngx_init_cycle(&init_cycle); if (cycle == NULL) { if (ngx_test_config) { ngx_log_stderr(0, "configuration file %s test failed", init_cycle.conf_file.data); } return 1; } ... ngx_cycle = cycle; }
其中配置解析在ngx_init_cycle里面完成
ngx_cycle_t * ngx_init_cycle(ngx_cycle_t *old_cycle) { ngx_cycle_t *cycle, **old; ... log = old_cycle->log; //这个pool放在cycle中带出去,配置解析之后赋值给ngx_cycle pool = ngx_create_pool(NGX_CYCLE_POOL_SIZE, log); if (pool == NULL) { return NULL; } pool->log = log; cycle = ngx_pcalloc(pool, sizeof(ngx_cycle_t)); if (cycle == NULL) { ngx_destroy_pool(pool); return NULL; } cycle->pool = pool; cycle->log = log; cycle->old_cycle = old_cycle; ..... 配置解析 ... return cycle; }
结论:ngx_cycle的周期不能简单的认为是从进程启动到退出,其pool在配置解析前后是两个值。在配置处理阶段ngx_cycle表示的是老的cycle,启动阶段完成才会换成新的cycle,这也是为什么在启动阶段调用ngx_http_cycle_get_module_main_conf会把设备搞挂的原因。
问题2:检视代码的时候发现ngx_array_t如果想被反复使用的话,需要注意其内存池的唯一性,不然会内存泄漏。
直接上nginx内核源码
void ngx_array_destroy(ngx_array_t *a) { ngx_pool_t *p; p = a->pool; if ((u_char *) a->elts + a->size * a->nalloc == p->d.last) { p->d.last -= a->size * a->nalloc; } if ((u_char *) a + sizeof(ngx_array_t) == p->d.last) { p->d.last = (u_char *) a; } }
结论:如果一个nginx的动态数组想在使用过程中调用ngx_array_destroy销毁,然后再ngx_array_init重复利用的话,那么一定要保证这个数组的内存池只给他自己用,否则,ngx_array_destroy中的两个判断都不成立,内存池的这块内存将无法释放。