http://blog.xiuwz.com/2011/11/29/nginx-pcre-conflict/
背景:最近新开发一个Nginx扩展模块,需要结合另外一个C++库进行扩展开发,该C++库中用到了pcre库。在编译后,发现总是会core,日志中出现明显如下的错误“pcre(****) failed to get memory”。针对该C++库,单独编写额外的程序处理,则不会出现任何问题。所以问题很诡异。
针对该问题,需要了解两个方面,1、pcre库。2、nginx中pcre库的处理(编译过nginx的都支持,一般情况下nginx都依赖到了pcre)。
pcre库相关
通过学习pcre库的api:http://regexkit.sourceforge.net/Documentation/pcre/pcreapi.html
基本上了解到pcre库中会动态申请相关内存,为了保证内存的线程安全,pcre提供了以下内存管理的函数指针:
1
2
3
4
5
|
void
*(*pcre_malloc)(
size_t
);
void
(*pcre_free)(
void
*);
void
*(*pcre_stack_malloc)(
size_t
);
void
(*pcre_stack_free)(
void
*);
int
(*pcre_callout)(pcre_callout_block *);
|
默认情况下,都直接采用了原生的malloc和free。
1
2
3
4
5
|
PCRE_EXP_DATA_DEFN
void
*(*pcre_malloc)(
size_t
) =
malloc
;
PCRE_EXP_DATA_DEFN
void
(*pcre_free)(
void
*) =
free
;
PCRE_EXP_DATA_DEFN
void
*(*pcre_stack_malloc)(
size_t
) =
malloc
;
PCRE_EXP_DATA_DEFN
void
(*pcre_stack_free)(
void
*) =
free
;
PCRE_EXP_DATA_DEFN
int
(*pcre_callout)(pcre_callout_block *) = NULL;
|
Nginx中Pcre处理
Nginx内部对Pcre库进行了再次封装,具体在src/core/ngx_regex.c文件中。nginx_regex_*函数基本上对pcre所有的接口都进行了二次封装。其中最关键的是对内存的处理,该内存处理采用了Nginx内置的内存池概念。
具体如下,首先,重置pcre内存管理的接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
void
ngx_regex_init(
void
)
{
pcre_malloc = ngx_regex_malloc;
pcre_free = ngx_regex_free;
}
static
void
* ngx_libc_cdecl ngx_regex_malloc(
size_t
size)
{
ngx_pool_t *pool;
#if (NGX_THREADS)
//线程相关处理
#else
pool = ngx_pcre_pool;
#endif
if
(pool) {
return
ngx_palloc(pool, size);
}
return
NULL;
}
static
void
ngx_libc_cdecl ngx_regex_free(
void
*p)
{
return
;
}
|
然后,在需要用到的地方进行初始化调用。
1
2
3
4
5
6
7
8
9
10
11
|
ngx_int_t
ngx_regex_compile(ngx_regex_compile_t *rc)
{
...
ngx_regex_malloc_init(rc->pool);
re = pcre_compile((
const
char
*) rc->pattern.data, (
int
) rc->options,
&errstr, &erroff, NULL);
/* ensure that there is no current pool */
ngx_regex_malloc_done();
...
}
|
结论
至此,比较清楚问题是出在Nginx的pcre内存管理和第三方默认的pcre内存管理有了冲突。所以解决方案就是在调用第三方的的地方进行pcre内存函数指针重置。如下所示:
1
2
3
4
5
6
7
8
9
|
void
*(*old_pcre_malloc)(
size_t
);
void
(*old_pcre_free)(
void
*);
old_pcre_malloc = pcre_malloc;
old_pcre_free = pcre_free;
pcre_malloc =
malloc
;
pcre_free =
free
;
/**调用第三方库的函数接口*/
pcre_malloc = old_pcre_malloc;
pcre_free = old_pcre_free;
|