Libevent

libevent内存管理

Posted by Liangjf on April 14, 2019

开启自定义内存分配

在一开始配置编译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.开启多线程时,又定义自定义分配内存函数,自定义内存分配函数也是要线性安全的。