当前位置: 首页 > 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);}

http://www.xdnf.cn/news/1003393.html

相关文章:

  • AtCoder-ABC-409 题解
  • java BIO/NIO/AIO
  • 工具+服务双驱动:创客匠人打造中医IP差异化竞争力
  • 搭建商城系统可能运用到的技术
  • Python告别数据处理卡顿之itertools模块使用详解
  • 立即体验|效果好、低延迟,Trae 已支持 Doubao-1.5-thinking-pro 新模型
  • faiss上的GPU流程,GPU与CPU之间的联系
  • MCP与FunctionCall的区别
  • HALCON第七讲->标定
  • 西电【计算机与网络安全实验】课程期末复习遗留情报
  • git添加全局忽略.DS_Store文件
  • MySQL 和 PostgreSQL,到底选择哪个?
  • 英语作文模板
  • 第八节 工程化与高级特性-模块与命名空间的选择
  • 道可云人工智能每日资讯|雄安人工智能产业园正式开园
  • 循环的嵌套
  • Chroma 向量数据库学习笔记
  • DAY49
  • Vue.js 从入门到实战:用户管理分页表格项目详解
  • 新书速览|CUDA并行编程与性能优化
  • Java大厂面试真题:谢飞机的技术挑战
  • 快速排序:分治思想的经典实践
  • 数据结构 - Java 队列
  • react中hook和高阶组件的选型
  • Windows安装docker及使用
  • nginx学习
  • 【Qt】如何使用QtInstallerFramework打包Qt程序
  • OpenCV CUDA模块图像变形------对图像进行上采样操作函数pyrUp()
  • 134. Gas Station
  • 画图使用说明书