开启自定义内存分配
在一开始配置编译Libevent库是,不能加入--disable-malloc-replacement
选项。
默认情况下,是没有这个选项的。如果加入了这个选项,将会在生成的event-config.h
中,定义EVENT__DISABLE_MM_REPLACEMENT
这个宏。
libevent的内粗怒分配在mm-internal.h文件。顺便提一下,所有libevent内部使用的模块等都会以xxx-internal命名。
#ifndef EVENT__DISABLE_MM_REPLACEMENT
EVENT2_EXPORT_SYMBOL
void *event_mm_malloc_(size_t sz);
EVENT2_EXPORT_SYMBOL
void *event_mm_calloc_(size_t count, size_t size);
EVENT2_EXPORT_SYMBOL
char *event_mm_strdup_(const char *str);
EVENT2_EXPORT_SYMBOL
void *event_mm_realloc_(void *p, size_t sz);
EVENT2_EXPORT_SYMBOL
void event_mm_free_(void *p);
#define mm_malloc(sz) event_mm_malloc_(sz)
#define mm_calloc(count, size) event_mm_calloc_((count), (size))
#define mm_strdup(s) event_mm_strdup_(s)
#define mm_realloc(p, sz) event_mm_realloc_((p), (sz))
#define mm_free(p) event_mm_free_(p)
#else
#define mm_malloc(sz) malloc(sz)
#define mm_calloc(n, sz) calloc((n), (sz))
#define mm_strdup(s) strdup(s)
#define mm_realloc(p, sz) realloc((p), (sz))
#define mm_free(p) free(p)
#endif
从这里也可以看到,如果定义了 EVENT__DISABLE_MM_REPLACEMENT
,那么就会实际调用自定义的内存分配接口。只不过宏定义,统一函数名字。
当然啦,即使是在编译的时候选择了禁止默认的分配器,如果没有定义自己的内存分配函数等,也是会调用c自带的malloc等。看下面的例子。
void *event_mm_malloc_(size_t sz)
{
if (sz == 0)
return NULL;
if (mm_malloc_fn_) //这里为空,就是没有自定义的内存分配
return mm_malloc_fn_(sz);
else
return malloc(sz);
}
自定义内存分配的函数都在event.c中,看下面。也是通过EVENT__DISABLE_MM_REPLACEMENT
这个宏来控制的。
#ifndef EVENT__DISABLE_MM_REPLACEMENT
static void *(*mm_malloc_fn_)(size_t sz) = NULL;
static void *(*mm_realloc_fn_)(void *p, size_t sz) = NULL;
static void (*mm_free_fn_)(void *p) = NULL;
void *event_mm_malloc_(size_t sz)
{
if (sz == 0)
return NULL;
if (mm_malloc_fn_)
return mm_malloc_fn_(sz);
else
return malloc(sz);
}
void *event_mm_calloc_(size_t count, size_t size)
{
if (count == 0 || size == 0)
return NULL;
if (mm_malloc_fn_) {
size_t sz = count * size;
void *p = NULL;
if (count > EV_SIZE_MAX / size)
goto error;
p = mm_malloc_fn_(sz);
if (p)
return memset(p, 0, sz);
} else {
void *p = calloc(count, size);
#ifdef _WIN32
/* Windows calloc doesn't reliably set ENOMEM */
if (p == NULL)
goto error;
#endif
return p;
}
error:
errno = ENOMEM;
return NULL;
}
char *event_mm_strdup_(const char *str)
{
if (!str) {
errno = EINVAL;
return NULL;
}
if (mm_malloc_fn_) {
size_t ln = strlen(str);
void *p = NULL;
if (ln == EV_SIZE_MAX)
goto error;
p = mm_malloc_fn_(ln+1);
if (p)
return memcpy(p, str, ln+1);
} else
#ifdef _WIN32
return _strdup(str);
#else
return strdup(str);
#endif
error:
errno = ENOMEM;
return NULL;
}
void *event_mm_realloc_(void *ptr, size_t sz)
{
if (mm_realloc_fn_)
return mm_realloc_fn_(ptr, sz);
else
return realloc(ptr, sz);
}
void event_mm_free_(void *ptr)
{
if (mm_free_fn_)
mm_free_fn_(ptr);
else
free(ptr);
}
void event_set_mem_functions(void *(*malloc_fn)(size_t sz),
void *(*realloc_fn)(void *ptr, size_t sz),
void (*free_fn)(void *ptr))
{
mm_malloc_fn_ = malloc_fn;
mm_realloc_fn_ = realloc_fn;
mm_free_fn_ = free_fn;
}
#endif
重点的是event_set_mem_functions
,通过这个函数来设置对应的自定义内存分配。其实就是把函数指针传进来,对mm_malloc_fn_
,mm_realloc_fn_
,mm_free_fn_
这三个函数指针赋值。
static void *(*mm_malloc_fn_)(size_t sz) = NULL;
static void *(*mm_realloc_fn_)(void *p, size_t sz) = NULL;
static void (*mm_free_fn_)(void *p) = NULL;
这个三个指针,要么全设为NULL(恢复默认状态),要么全部都非NULL。想想,要么就是都使用默认的,要么就都使用自定义的。不然会容易出现不一致的行为,所以是禁止的了。
定制自定义内存分配函数需要注意的地方:
- 1.和C标准库的malloc分配的内存的地址对齐(libevent使用的分配内存函数肯定和应用程序一致)
- 2.它们的语义要和C标准库的一致。
- 3.开启多线程时,又定义自定义分配内存函数,自定义内存分配函数也是要线性安全的。