王坛蜜首先介绍一下用到的重要的结构体-标记内存块,在每个存放数据的内存块前都会有一个这样的标记结构体。
里面有两个变量,pxNextFreeBlock指向下一个内存块,xBlockSize用来表示它所标记的内存块大小。
经过一系列的操作,使初始化后,空闲内存表的起始地址为字节对齐,这里和heap_2不同的地方是,使用了临时变量uxAddress存储中间计算出来的一些地址,这里uxAddress存储的是字节对齐后的初始地址,然后赋值给pucAlignedHeap变量中。
这部分代码用来初始化空闲内存表的头和尾,使头的下一个内存块指向字节对齐后的首地址,大小初始化为0;这里的uxAddress变量经过一系列操作以及变成了内存块的末地址,然后使尾的首地址指向末地址(pxEnd=(void *)uxAddress),大小初始化为0,尾的下一个内存块为NULL。
在申请内存的最开始,把整个内存堆都看成一个整体,作为一个大内存块,这个内存块之前也需要有一个标记结构体,也就是pxFirstFreeBlock结构体,这里对这个结构体进行初始化,它的首地址就是字节对齐后的地址,大小是尾地址uxAddess-内存块字节对齐后的首地址,下一个内存块指向pxEnd。
先定义两个用到的局部变量,pxIterator相当于C++中容器的迭代器,puc就是个临时变量。
这里就是使用迭代器一次次循环,知道找到空闲内存表中满足内存要求(pxIterator->
pxNextFreeBlock
这里是判断要插入的这块内存和前一块内存是否相邻,如果相邻就合并成一块,判断是否相邻的条件是puc + pxIterator->
xBlockSize ) == ( uint8_t * ) pxBlockToInsert,插入点地址+这块内存的大小==要插入块首地址;即上一块末地址==要插入块起始地址
判断一下想要插入数据的内存块是否被使用,就是和xBlockAllocateBit变量做一次与运算,如果结果不是1,则说明没被使用;在确保要插入的大小大于0之后,需要附加上标记结构体的大小(8字节)后,再进行字节对齐。
如果找到的是pxEnd表示没有内存可以分配,否则就将内存首地址保存在 pvReturn 中,函数返回的时候返回此值,然后将这块内存从空闲内存表中删除
申请的内存大小小于空闲的一大块内存的大小,则将其分割,剩下的留着,相当于给空闲内存块的首地址做一个地址偏移:新的空闲内存块=满足要求的内存块首地址+需要的内存块首地址,然后更新新的空闲内存块的大小,并将其插入到空闲内存表。
传入的数据地址没包含标志结构体,需要先做减法,进行地址移位,然后将包含了标志结构体的首地址保存在pxLink中
如果要释放的内存真的被使用,就开始释放操作,先把首位变0,表示变成空闲,然后更新空闲内存大小,将这块内存插入回空闲内存表中,要注意:释放和申请内存,并不是把这块内存从一个链表中拿出来了,只是做了一些标记,让程序知道这部分被占用,有数据,在释放内存之前我们将数据删除,然后把标志位改为空闲状态就行,这就是释放的本质。
其他的函数主要就是直接返回我们之前更新的全局变量,终于把基础知识都铺垫完了,下面结合具体项目程序谈谈怎么优化了。
|