现在的位置: 首页 > 综合 > 正文

深入解析Nginx的pcre库及相关注意事项

2013年02月15日 ⁄ 综合 ⁄ 共 2051字 ⁄ 字号 评论关闭

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;

抱歉!评论已关闭.