1、前言
因近日到一家公司面试,自诩读过lwip源码,被问到lwip是如何进行内存管理的,恍惚间只记得内存块,内存池的概念,但时隔多日,具体内容早已抛到九霄云外了,于是记下,以便加深记忆。
2、背景
在平时编程中,经常会需要进行动态内存申请的操作,创建一个临时数据区域,在程序某个阶段结束后,释放该区域。
例如C语言中用malloc()函数申请内存,free()函数释放内存,C++中利用new在堆上动态创建一个对象,用delete释放内存,值得注意的是,在这里malloc()是一个功能函数,new是一个操作符。
在用户进行动态内存申请,释放的过程中,经过一系列的运行后,就会在内存块上出现部分已用,部分被释放未用,这样就导致,即式内存块有较大的未用空间,当申请一个空间较大的对象时,会出现申请不成功。
3、常见的内存分配策略
(1)系统规定用户在申请内存时,申请大小必须指定为某几个固定的值,否则分配函数不予分配;
(2)系统在启动时,也会在内存中初始几个固定大小的内存块链表,系统把用户在运行期间可能会请求到的内存大小划分为几个范围;这种方式会造成空间上的浪费,但可以达到很高的分配速度;
(3)系统运行时,各个空闲块的大小随着系统运行而改变。
4、lwip的内存分配策略
(1)动态内存池
在lwip中,这种方式主要用于固定数据结构的分配,例如UDP控制块、TCP控制块等;
内核在初始化时,已经为每个数据结构都初始化好一定数据的POOL,把协议中所有的POOL挨个放到一起,并把他们放在一块连续的区域,这形成一个大的缓存池。
与内存池管理相关的系统函数主要有三个:
内存池初始化函数memp_init、内存池分配函数memp_malloc、内存池分配函数memp_free
(2)动态内存堆
动态内存堆分配策略本质上是对一个事先定义好的内存块进行合理有效的组织和管理,其内存分配的策略采用首次拟合方式,只要找到一个比用户空间请求空间大的空闲块,从中切割出合适的块,把剩余的部分返回到动态内存堆中。
在这种策略下,用户申请内存块大小具有最小限制,即请求的大小不能小于MIN_SIZE,否则系统自动请求大小设置为MIN_SIZE。通常用户可以自定义该值达到节省内存空间的目的,不过会导致大的内存块不断被分成小的内存块,内存释放时的过程则相反,内存函数会查看该节点前后的内存块是否空闲,如果空闲则合成一个大的内存空闲块。
采用这种方式其优点时内存浪费小,比较简单,适合小内存。其缺点是如果频繁的动态内存分配和释放,可能会造成严重的内存碎片,如果在内存碎片严重的情况下,可能会导致内存分配不成功。