当前位置: 首页 > news >正文

Nuttx之mm_realloc

声明:此处代码分析,来源与 nuttx 12.8.0版本。

/***************************************************************************** Name: mm_realloc** Description:*   If the reallocation is for less space, then:**     (1) the current allocation is reduced in size*     (2) the remainder at the end of the allocation is returned to the*         free list.**  If the request is for more space and the current allocation can be*  extended, it will be extended by:**     (1) Taking the additional space from the following free chunk, or*     (2) Taking the additional space from the preceding free chunk.*     (3) Or both**  If the request is for more space but the current chunk cannot be*  extended, then malloc a new buffer, copy the data into the new buffer,*  and free the old buffer.*****************************************************************************/FAR void *mm_realloc(FAR struct mm_heap_s *heap, FAR void *oldmem,size_t size)
{FAR struct mm_allocnode_s *oldnode;FAR struct mm_freenode_s  *prev = NULL;FAR struct mm_freenode_s  *next;size_t newsize;size_t oldsize;size_t prevsize = 0;size_t nextsize = 0;FAR void *newmem;/* If oldmem is NULL, then realloc is equivalent to malloc */if (oldmem == NULL){return mm_malloc(heap, size);}DEBUGASSERT(mm_heapmember(heap, oldmem));#ifdef CONFIG_MM_HEAP_MEMPOOLif (heap->mm_mpool){newmem = mempool_multiple_realloc(heap->mm_mpool, oldmem, size);if (newmem != NULL){return newmem;}else if (size <= heap->mm_threshold ||mempool_multiple_alloc_size(heap->mm_mpool, oldmem) >= 0){newmem = mm_malloc(heap, size);if (newmem != NULL){memcpy(newmem, oldmem,MIN(size, mm_malloc_size(heap, oldmem)));mm_free(heap, oldmem);}return newmem;}}
#endif/* Adjust the size to account for (1) the size of the allocated node and* (2) to make sure that it is aligned with MM_ALIGN and its size is at* least MM_MIN_CHUNK.*/if (size < MM_MIN_CHUNK - MM_ALLOCNODE_OVERHEAD){size = MM_MIN_CHUNK - MM_ALLOCNODE_OVERHEAD;}newsize = MM_ALIGN_UP(size + MM_ALLOCNODE_OVERHEAD);if (newsize < size){/* There must have been an integer overflow */DEBUGPANIC();return NULL;}/* Map the memory chunk into an allocated node structure */oldnode = (FAR struct mm_allocnode_s *)((FAR char *)kasan_reset_tag(oldmem) - MM_SIZEOF_ALLOCNODE);/* We need to hold the MM mutex while we muck with the nodelist. */DEBUGVERIFY(mm_lock(heap));DEBUGASSERT(MM_NODE_IS_ALLOC(oldnode));/* Check if this is a request to reduce the size of the allocation. */oldsize = MM_SIZEOF_NODE(oldnode);if (newsize <= oldsize){/* Handle the special case where we are not going to change the size* of the allocation.*/if (newsize < oldsize){heap->mm_curused += newsize - oldsize;mm_shrinkchunk(heap, oldnode, newsize);kasan_poison((FAR char *)oldnode + MM_SIZEOF_NODE(oldnode) +sizeof(mmsize_t), oldsize - MM_SIZEOF_NODE(oldnode));}/* Then return the original address */mm_unlock(heap);MM_ADD_BACKTRACE(heap, oldnode);return oldmem;}/* This is a request to increase the size of the allocation,  Get the* available sizes before and after the oldnode so that we can make the* best decision*/next = (FAR struct mm_freenode_s *)((FAR char *)oldnode + oldsize);if (MM_NODE_IS_FREE(next)){DEBUGASSERT(MM_PREVNODE_IS_ALLOC(next));nextsize = MM_SIZEOF_NODE(next);}if (MM_PREVNODE_IS_FREE(oldnode)){prev = (FAR struct mm_freenode_s *)((FAR char *)oldnode - oldnode->preceding);DEBUGASSERT(MM_NODE_IS_FREE(prev));prevsize = MM_SIZEOF_NODE(prev);}/* Now, check if we can extend the current allocation or not */if (nextsize + prevsize + oldsize >= newsize){size_t needed = newsize - oldsize;size_t nodesize = oldsize;size_t takeprev;size_t takenext;/* Check if we can extend into the previous chunk and if the* previous chunk is smaller than the next chunk.*/if (nextsize > prevsize){/* Can we get everything we need from the previous chunk? */if (needed > prevsize){/* No, take the whole previous chunk and get the* rest that we need from the next chunk.*/takeprev = prevsize;takenext = needed - prevsize;}else{/* Yes, take what we need from the previous chunk */takeprev = needed;takenext = 0;}}/* Check if we can extend into the next chunk and if we still need* more memory.*/else{/* Can we get everything we need from the next chunk? */if (needed > nextsize){/* No, take the whole next chunk and get the rest that we* need from the previous chunk.*/takeprev = needed - nextsize;takenext = nextsize;}else{/* Yes, take what we need from the previous chunk */takeprev = 0;takenext = needed;}}/* Extend into the previous free chunk */newmem = oldmem;if (takeprev){FAR struct mm_allocnode_s *newnode;/* Remove the previous node.  There must be a predecessor, but* there may not be a successor node.*/DEBUGASSERT(prev && prev->blink);prev->blink->flink = prev->flink;if (prev->flink){prev->flink->blink = prev->blink;}/* Make sure the new previous node has enough space */if (prevsize < takeprev + MM_MIN_CHUNK){heap->mm_curused += prevsize - takeprev;takeprev          = prevsize;}/* Extend the node into the previous free chunk */newnode = (FAR struct mm_allocnode_s *)((FAR char *)oldnode - takeprev);/* Did we consume the entire preceding chunk? */if (takeprev < prevsize){/* No.. just take what we need from the previous chunk and put* it back into the free list*/prevsize          -= takeprev;prev->size         = prevsize | (prev->size & MM_MASK_BIT);nodesize          += takeprev;newnode->size      = nodesize | MM_ALLOC_BIT | MM_PREVFREE_BIT;newnode->preceding = prevsize;/* Return the previous free node to the nodelist* (with the new size)*/mm_addfreechunk(heap, prev);}else{/* Yes.. update its size (newnode->preceding is already set) */nodesize     += prevsize;newnode->size = nodesize | MM_ALLOC_BIT |(newnode->size & MM_MASK_BIT);}newmem = (FAR void *)((FAR char *)newnode + MM_SIZEOF_ALLOCNODE);/* Now we want to return newnode */oldnode = newnode;}/* Extend into the next free chunk */if (takenext){FAR struct mm_freenode_s *newnode;FAR struct mm_allocnode_s *andbeyond;/* Get the chunk following the next node (which could be the tail* chunk)*/andbeyond = (FAR struct mm_allocnode_s *)((FAR char *)next + nextsize);/* Remove the next node.  There must be a predecessor, but there* may not be a successor node.*/DEBUGASSERT(next->blink);next->blink->flink = next->flink;if (next->flink){next->flink->blink = next->blink;}/* Make sure the new next node has enough space */if (nextsize < takenext + MM_MIN_CHUNK){heap->mm_curused += nextsize - takenext;takenext          = nextsize;}/* Extend the node into the next chunk */nodesize += takenext;oldnode->size = nodesize | (oldnode->size & MM_MASK_BIT);/* Did we consume the entire preceding chunk? */if (takenext < nextsize){/* No, take what we need from the next chunk and return it to* the free nodelist.*/newnode              = (FAR struct mm_freenode_s *)((FAR char *)oldnode + nodesize);newnode->size        = nextsize - takenext;andbeyond->preceding = newnode->size;/* Add the new free node to the nodelist (with the new size) */mm_addfreechunk(heap, newnode);}else{/* Yes, just update some pointers. */andbeyond->size &= ~MM_PREVFREE_BIT;}}/* Update heap statistics */heap->mm_curused += newsize - oldsize;if (heap->mm_curused > heap->mm_maxused){heap->mm_maxused = heap->mm_curused;}sched_note_heap(NOTE_HEAP_FREE, heap, oldmem, oldsize,heap->mm_curused - newsize);sched_note_heap(NOTE_HEAP_ALLOC, heap, newmem, newsize,heap->mm_curused);mm_unlock(heap);MM_ADD_BACKTRACE(heap, (FAR char *)newmem - MM_SIZEOF_ALLOCNODE);newmem = kasan_unpoison(newmem, MM_SIZEOF_NODE(oldnode) -MM_ALLOCNODE_OVERHEAD);if (kasan_reset_tag(newmem) != kasan_reset_tag(oldmem)){/* Now we have to move the user contents 'down' in memory.  memcpy* should be safe for this.*/memcpy(newmem, oldmem, oldsize - MM_ALLOCNODE_OVERHEAD);}return newmem;}/* The current chunk cannot be extended.* Just allocate a new chunk and copy*/else{/* Allocate a new block.  On failure, realloc must return NULL but* leave the original memory in place.*/mm_unlock(heap);newmem = mm_malloc(heap, size);if (newmem){memcpy(newmem, oldmem, oldsize - MM_ALLOCNODE_OVERHEAD);mm_free(heap, oldmem);}return newmem;}
}

显然,此函数用于改变获取的堆空间的大小。那空间大小怎么个变法呢?当然是伸缩自如。

/***************************************************************************** Name: mm_realloc** Description:*   If the reallocation is for less space, then:**     (1) the current allocation is reduced in size*     (2) the remainder at the end of the allocation is returned to the*         free list.**  If the request is for more space and the current allocation can be*  extended, it will be extended by:**     (1) Taking the additional space from the following free chunk, or*     (2) Taking the additional space from the preceding free chunk.*     (3) Or both**  If the request is for more space but the current chunk cannot be*  extended, then malloc a new buffer, copy the data into the new buffer,*  and free the old buffer.*****************************************************************************/

对于第一种情况,缩小已分配空间。判断是此情况后,调用函数mm_shrinkchunk即可完成。

  if (newsize <= oldsize){......mm_shrinkchunk(heap, oldnode, newsize);......}

mm_shrinkchunk会从oldnode的空间,将缩小的部分空间放入mm_heap_s 的mm_nodelist数组中。

对于第二种情况,扩展已分配空间。那,具体怎么个扩展法呢?正如注释所言,可向低地址扩展,也可向高地址扩展,还可以向高低两头扩展。我们现在说的都是在需要被调整的空间附近进行扩展,有人就要问了,附近是有多近?附近就是与被调整空间直接连着的空间。即扩展方向的空间与被调整空间在虚拟地址上是连续的。如果附近的空间不够怎么办呢?这就是扩展空间的另外一种情形,此时需要从mm_nodelist中完全重新找一段新的符合条件的空间。

反映到代码上是什么样呢。

首先判断确定附近的空间大小是否符合条件。

if (nextsize + prevsize + oldsize >= newsize)

这个判断包含了几种情况,具体是,nextsize == 0,只使用prevsize,prevsize == 0,只使用nextsize,nextsize与prevsize各使用一部分空间。因此接下需要对这几种情况进行区分。

      /* Check if we can extend into the previous chunk and if the* previous chunk is smaller than the next chunk.*/     if (nextsize > prevsize){/* Can we get everything we need from the previous chunk? */if (needed > prevsize){/* No, take the whole previous chunk and get the* rest that we need from the next chunk.*/......}else{/* Yes, take what we need from the previous chunk */......}}/* Check if we can extend into the next chunk and if we still need* more memory.*/else{/* Can we get everything we need from the next chunk? */if (needed > nextsize){/* No, take the whole next chunk and get the rest that we* need from the previous chunk.*/......}else{/* Yes, take what we need from the previous chunk */......}}

上述的if (nextsize > prevsize)自然包含了prevsize == 0的情况,且与if (needed > prevsize)实现了另外的一种效果,那就是,优先使用小空间。不得不说,这几个if站的很高。艺术水平至少三层楼那么高。

确定完 前后需要扩展的空间大小之后,接下来就是具体的空间合并过程。此时还有一个调整即将被合并的空间大小的操作。

/* Make sure the new previous node has enough space */if (prevsize < takeprev + MM_MIN_CHUNK){heap->mm_curused += prevsize - takeprev;takeprev          = prevsize;}/* Extend the node into the previous free chunk */newnode = (FAR struct mm_allocnode_s *)((FAR char *)oldnode - takeprev);

此处的if判断用于确定prevsize被合并后,剩余的空间是否能够有资格形成一个新的mm_freenode_s。何以见得呢? mm_addfreechunk函数会给出答案。

static inline_function void mm_addfreechunk(FAR struct mm_heap_s *heap,FAR struct mm_freenode_s *node)
{......size_t nodesize = MM_SIZEOF_NODE(node);......DEBUGASSERT(nodesize >= MM_MIN_CHUNK);......
}

同时当if(prevsize < takeprev + MM_MIN_CHUNK)成立时,mm_realloc最终扩展后返回给用户的空间,可能比mm_realloc的参数size要求的空间更大。

最后,扩展后的空间,如果起始地址发生发生改变,就需要memcpy来进行内容拷贝了。

      if (kasan_reset_tag(newmem) != kasan_reset_tag(oldmem)){/* Now we have to move the user contents 'down' in memory.  memcpy* should be safe for this.*/memcpy(newmem, oldmem, oldsize - MM_ALLOCNODE_OVERHEAD);}

那, 附近空间不够用怎么办呢?交给mm_malloc就行了。

      newmem = mm_malloc(heap, size);if (newmem){memcpy(newmem, oldmem, oldsize - MM_ALLOCNODE_OVERHEAD);mm_free(heap, oldmem);}

相关文章:

  • MPMA:Preference Manipulation Attack Against Model Context Protocol
  • Java学习笔记之:Vue中路由的基本使用
  • OpenCV 多边形绘制与填充
  • 【鸿蒙开发】组件动态创建
  • CKA考试知识点分享(11)---CRD
  • 在 Vue 3 中修改 el-select 组件接收的 prop 值
  • reactor模型
  • vue下的xlsx文件导出和导入的写法
  • java(JDBC)
  • [Blender] 高质量材质推荐第四弹:25-30号精选纹理资源详解
  • [MSPM0开发]MSPM0G3507之GPIO输入、输出、中断使用(基于driverlib库)
  • 销售预测的方法与模型(二)丨商品与库存分类——基于数据模型运营的本质和底层逻辑销售
  • 机器学习算法——朴素贝叶斯和特征降维
  • 名称 深度学习(监督学习) Iteration 一次 mini-batch 前向+反向传播更新 Epoch 所有数据集训练一遍。这两个概念不一样吗?
  • 图像分割技术:像素级的精准识别(superior哥深度学习系列第12期)
  • Vue3项目与桌面端(C++)通过Websocket 对接接口方案实现
  • GPIO简介(GPIO输出)
  • Metastore 架构示意图和常用 SQL
  • HINet: Half Instance Normalization Network for Image Restoration论文阅读
  • 使用Optimization tool优化后禁用windows更新批量的脚本
  • 网站的建设怎么写/网络营销做的比较好的企业
  • 老房装修改造哪家好/seo关键词排名优化品牌
  • 软件测试培训包就业是真的吗/免费seo推广计划
  • 网站网页制作教程/企业品牌网站营销
  • 网站开发过程总结/推56论坛
  • 广东微信网站制作报价/网站seo优化外包顾问