目录

内存管理函数

函数声明

event-config.h

函数定义

event_mm_malloc_

event_mm_calloc_

event_mm_strdup_

event_mm_realloc_

event_mm_free_

event_set_mem_functions设置自定义内存管理函数

内存管理流程


以下源码均基于libevent-2.0.21-stable。

内存管理函数

函数声明

libevent的内存管理函数不是很多也不复杂,函数的声明放在mm-internal.h下面,如下所示:

可以发现,内存管理函数主要有5个:event_mm_malloc_、event_mm_calloc_、event_mm_strdup_、event_mm_realloc_和event_mm_free_,从函数名就能够大概猜出函数的作用,先不说这个,先来看另一个需要注意的地方。

所有内存管理函数的声明都是放在一个条件编译体内的,其编译条件为没有定义_EVENT_DISABLE_MM_REPLACEMENT,也就是说,如果程序中定义了_EVENT_DISABLE_MM_REPLACEMENT,那么内存管理函数将无法使用,那么libevent是如何去控制是否定义_EVENT_DISABLE_MM_REPLACEMENT的呢?这里就不得不说一下另一个很重要的头文件event-config.h。

event-config.h

如果仔细观察,就会发现,libevent中的大多数.c文件都包含了event-config.h头文件。从这个头文件名字来看,这应当是用来进行配置的,配置什么呢?先来看看头文件中的内容:

从上面展示的部分event-config.h内容来看,event-config.h实际上就是对程序中可能会用到的宏定义进行#define或者#undef,从而来控制编译行为。注意到event-config.h第21行代码,恰好就是前面所说的_EVENT_DISABLE_MM_REPLACEMENT。

        参考第20行的注释,如果不允许替换libevent提供的内存管理函数,那么就会定义_EVENT_DISABLE_MM_REPLACEMENT,在此情况下mm-internal.h将不会声明前面提到的内存管理函数也就无法使用了。如果允许替换则不定义,就可以使用那些内存管理函数。

那么如何设置是否允许替换libevent提供的内存管理函数呢?这是由一开始安装libevent库时./configure的选项决定的,在libevent库安装一文中,通过指定./configure的prefix来指定安装路径,当然,还有很多选项,直接打开configure文件(下载下来的文件中,非最终安装的文件中),其中一部分如下所示:

黄色高亮部分为执行./configure时可附加的选项 --disable-malloc-replacement,根据该选项的说明“disable support for replacing the memory mgt functions”可以知道,它就是用来设置是否允许替换内存管理函数的。

通过实践来证明一下:之前执行./configure时没有添加--disable-malloc-replacement,安装路径(非下载路径,下载路径中也有event-config.h)下的event-config.h中对_EVENT_DISABLE_MM_REPLACEMENT是未定义状态此时就可以替换内存管理函数。

现在重新将libevent库安装到另一个文件夹下,添加--disable-malloc-replacement选项,如下所示:

然后make && make  install,再到安装后的include文件夹下打开event-config.h,如下所示:

此时发现_EVENT_DISABLE_MM_REPLACEMENT被定义了,也就不能替换内存管理函数了。

以上证明:如果在./configure进行配置的时候添加了--disable-malloc-replacement选项,那么无法调用内存管理函数的。

通过以上分析,明白了内存管理函数的允许调用的条件,下面来看看内存管理函数的具体实现。

函数定义

内存管理函数的定义都位于event.c下。下面依次来看event_mm_malloc_、event_mm_calloc_、event_mm_strdup_、event_mm_realloc_和event_mm_free_五个函数。

event_mm_malloc_

根据event_mm_malloc_的名字可以大致猜出这个函数与malloc有关,函数定义如下:

  1. void *
  2. event_mm_malloc_(size_t sz)
  3. {
  4. if (_mm_malloc_fn)
  5. return _mm_malloc_fn(sz);
  6. else
  7. return malloc(sz);
  8. }

这里先对_mm_malloc_fn进行了判断,毫无疑问,这与libevent库日志及错误处理中所用到的log_fn和fatal_fn是类似的,查看_mm_malloc_fn的定义为:static void *(*_mm_malloc_fn)(size_t sz) = NULL;

也就是说,这里的_mm_malloc_fn是一个函数指针,当_mm_malloc_fn非空时,会直接以event_mm_malloc_的入参sz为参数调用_mm_malloc_fn所指向的函数。如果_mm_malloc_fn为空,执行的就是默认处理行为了(_mm_malloc_fn初始值为NULL)。这里的默认处理行为是直接调用malloc函数,根据event_mm_malloc_的入参sz,在内存中开辟一段连续的大小为sz的空间,并且返回指向这段空间的泛型指针。

可想而知,_mm_malloc_fn所指向的函数至少需要保留malloc函数的功能

event_mm_calloc_

event_mm_calloc_函数定义如下:

  1. void *
  2. event_mm_calloc_(size_t count, size_t size)
  3. {
  4. if (_mm_malloc_fn) {
  5. size_t sz = count * size;
  6. void *p = _mm_malloc_fn(sz);
  7. if (p)
  8. memset(p, 0, sz);
  9. return p;
  10. } else
  11. return calloc(count, size);
  12. }

这里依然会先判断_mm_malloc_fn,先来看_mm_malloc_fn为空情况下的默认处理行为,event_mm_calloc_会直接调用calloc函数。calloc函数与malloc函数相似,都是开辟一段连续的空间,不同点在于calloc函数需要传入count和size两个参数,用来开辟count个大小为size的连续空间(总大小为count*size),并且为这些空间全部赋值为0。然后返回指向这段空间的泛型指针。

再来看_mm_malloc_fn非空的情形,会先计算开辟空间总大小sz,然后调用_mm_malloc_fn所指向的函数,而调用的函数也应当实现malloc的效果,开辟一段空间,然后再用memset对这段空间初始化为0,并返回指向这段空间的泛型指针。

也就是说,这里_mm_malloc_fn非空的情形下需要保留calloc的功能。

event_mm_strdup_

event_mm_strdup_函数定义如下:

  1. char *
  2. event_mm_strdup_(const char *str)
  3. {
  4. if (_mm_malloc_fn) {
  5. size_t ln = strlen(str);
  6. void *p = _mm_malloc_fn(ln+1);
  7. if (p)
  8. memcpy(p, str, ln+1);
  9. return p;
  10. } else
  11. #ifdef WIN32
  12. return _strdup(str);
  13. #else
  14. return strdup(str);
  15. #endif
  16. }

有了前面两个函数的了解,对于event_mm_strdup_,默认需要实现的功能是strdup,strdup是指新开辟一段空间,并将传入的字符串复制到新开辟的空间中,并返回这段空间的指针。

        因此,在_mm_malloc_fn非空的情形下也需要保留strdup的功能。这里实现的方法是:先获取字符串长度,再加上一个终止符作为总长度,开辟该长度的空间,并取得指向该空间的指针,然后将字符串复制到这段空间。

event_mm_realloc_

  1. void *
  2. event_mm_realloc_(void *ptr, size_t sz)
  3. {
  4. if (_mm_realloc_fn)
  5. return _mm_realloc_fn(ptr, sz);
  6. else
  7. return realloc(ptr, sz);
  8. }

和上面一样,先来看realloc有什么功能:realloc用来改变一段内存的大小,传入两个参数,一个指向原空间的指针ptr,以及需要开辟的新空间的大小sz。一般情况下sz会比ptr本身指向的连续空间的大小大,realloc会先判断当前的指针是否有足够的连续空间,如果有,扩大ptr指向的地址,并且将ptr返回如果空间不够,先按照sz指定的大小分配空间,将原有数据从头到尾拷贝到新分配的内存区域,而后释放原来ptr所指内存区域(注意:ptr是自动释放,不需要使用free),同时返回新分配的内存区域的首地址。即重新分配的空间的地址。

        然后再来看,这里判断的不再是_mm_malloc_fn,而是_mm_realloc_fn,_mm_realloc_fn实际上和_mm_malloc_fn相似,也是一个函数指针,可以通过它来调用自定义函数。毫无疑问,_mm_realloc_fn也应当至少保留realloc函数的功能。

event_mm_free_

  1. void
  2. event_mm_free_(void *ptr)
  3. {
  4. if (_mm_free_fn)
  5. _mm_free_fn(ptr);
  6. else
  7. free(ptr);
  8. }

event_mm_free_实现的功能就是释放ptr所指向的内存空间。这里判断的是_mm_free_fn,也是一个函数指针,其指向的函数应保留free的功能。其他的和前面类似。

event_set_mem_functions设置自定义内存管理函数

通过上面的分析,自定义的内存管理函数是通过三个函数指针实现的:_mm_malloc_fn、_mm_realloc_fn和_mm_free_fn,其中_mm_malloc_fn指向的函数应实现分配空间的功能;_mm_realloc_fn指向的函数应实现对已分配空间重新分配大小的功能;_mm_free_fn指向的函数应实现释放已分配空间的功能。

自定义内存管理函数的设置是通过event_set_mem_functions函数实现的,如下所示:

  1. void
  2. event_set_mem_functions(void *(*malloc_fn)(size_t sz),
  3. void *(*realloc_fn)(void *ptr, size_t sz),
  4. void (*free_fn)(void *ptr))
  5. {
  6. _mm_malloc_fn = malloc_fn;
  7. _mm_realloc_fn = realloc_fn;
  8. _mm_free_fn = free_fn;
  9. }

该函数与设置自定义的日志及错误处理函数相似,只不过这里需要同时设置三个函数,对应于_mm_malloc_fn、_mm_realloc_fn和_mm_free_fn。

内存管理流程

转载自:https://blog.csdn.net/qq_28114615/article/details/89236244

libevent源码学习(2):内存管理的更多相关文章

  1. Python 源码学习之内存管理 -- (转)

    Python 的内存管理架构(Objects/obmalloc.c): _____ ______ ______ ________ [ int ] [ dict ] [ list ] ... [ str ...

  2. Memcached源码分析之内存管理

    先再说明一下,我本次分析的memcached版本是1.4.20,有些旧的版本关于内存管理的机制和数据结构与1.4.20有一定的差异(本文中会提到). 一)模型分析在开始解剖memcached关于内存管 ...

  3. libevent源码学习

    怎么快速学习开源库比如libevent? libevent分析 - sparkliang的专栏 - 博客频道 - CSDN.NET Libevent源码分析 - luotuo44的专栏 - 博客频道 ...

  4. libevent源码学习(11):超时管理之min_heap

    目录min_heap的定义向min_heap中添加eventmin_heap中event的激活以下源码均基于libevent-2.0.21-stable.       在前文中,分析了小顶堆min_h ...

  5. libevent源码学习(10):min_heap数据结构解析

    min_heap类型定义min_heap函数构造/析构函数及初始化判断event是否在堆顶判断两个event之间超时结构体的大小关系判断堆是否为空及堆大小返回堆顶event分配堆空间堆元素的上浮堆元素 ...

  6. libevent源码学习(9):事件event

    目录在event之前需要知道的event_baseevent结构体创建/注册一个event向event_base中添加一个event设置event的优先级激活一个event删除一个event获取指定e ...

  7. redis源码解析之内存管理

    zmalloc.h的内容如下: void *zmalloc(size_t size); void *zcalloc(size_t size); void *zrealloc(void *ptr, si ...

  8. Mybatis源码学习之事务管理(八)

    简述 在实际开发中,数据库事务的控制是一件非常重要的工作,本文将学习Mybatis对事务的管理机制.在Mybatis中基于接口 Transaction 将事务分为两种,一种是JdbcTransacti ...

  9. Samba 源码解析之内存管理

    由于工作需要想研究下Samba的源码,下载后发现目录结构还是很清晰的.一般大家可能会对source3和source4文件夹比较疑惑.这两个文件夹针对的是Samba主版本号,所以你可以暂时先看一个.这里 ...

随机推荐

  1. setoolkit的钓鱼实验

    1.在kali中打开setoolkit 2.在菜单中选择第一个进入社会工程学攻击 3.选择第二个模块属于网站攻击向量 4.选择第五个模块,进行web劫持攻击 5.选择第二个,进行网站克隆 6.发现访问 ...

  2. 【AGC052A】

    题目 \(有T组询问\) \(每次给一个n\) \(3个01串\),\(这三个串每个都有n个0\),\(n个1\),\(求一个2*n + 1的字符串\),\(使他成为S1+S1,S2+S2,S3+S3 ...

  3. UOJ #228 - 基础数据结构练习题(势能线段树+复杂度分析)

    题面传送门 神仙题. 乍一看和经典题 花神游历各国有一点像,只不过多了一个区间加操作.不过多了这个区间加操作就无法再像花神游历各国那样暴力开根直到最小值为 \(1\) 为止的做法了,稍微感性理解一下即 ...

  4. Matlab混合编程

    Matlab混合编程 混合编程目的 在Matlab中采用混合编程目的主要包括 利用已有的函数库,避免重复工作 加速计算,特别是减少循环所用时间 利用GPU等进行异构编程 混合编程方法-mex函数 目前 ...

  5. 【GS文献】基因组选择技术在农业动物育种中的应用

    中国农业大学等多家单位2017年合作发表在<遗传>杂志上的综述,笔记之. 作者中还有李宁院士,不胜唏嘘. 1.概述 GS的两大难题:基因组分型的成本,基因组育种值(genomic esti ...

  6. R 语言实战-Part 3 笔记

    R 语言实战(第二版) part 3 中级方法 -------------第8章 回归------------------ #概念:用一个或多个自变量(预测变量)来预测因变量(响应变量)的方法 #最常 ...

  7. 【机器学习与R语言】8- 神经网络

    目录 1.理解神经网络 1)基本概念 2)激活函数 3)网络拓扑 4)训练算法 2.神经网络应用示例 1)收集数据 2)探索和准备数据 3)训练数据 4)评估模型 5)提高性能 1.理解神经网络 1) ...

  8. js判断undefined nan等

    1,js判断undefined 主要用typeof(),typeof的返回值有:undefined,object,boolean,number,string,symbol,function等, if( ...

  9. TensorFlow 2.0 深度学习实战 —— 浅谈卷积神经网络 CNN

    前言 上一章为大家介绍过深度学习的基础和多层感知机 MLP 的应用,本章开始将深入讲解卷积神经网络的实用场景.卷积神经网络 CNN(Convolutional Neural Networks,Conv ...

  10. 日常Java 2021/9/23

    练习使用Math.random函数,以及JOptionPane.showMessageDialog(null,"字符串","Results",JOptionPa ...