目录
一、步骤1-判断边界情况,realloc也可以执行malloc和free的功能
二、步骤2-原chunk如果MMAP方式分配,申请新内存并拷贝实现
三、步骤3-非MMAP方式分配,则_int_realloc进行合并/裁剪等实现
1. _int_realloc函数:老chunk足够大,则裁剪
2. _int_realloc函数:尝试从Top chunk上进行扩展
3. _int_realloc函数:尝试合并nextchunk空间切割
4. _int_realloc函数:调用_int_malloc函数分配新内存
5. _int_realloc函数:裁剪返回Userchunk释放Remainder chunk
四、步骤4-如果_int_realloc分配失败,则尝试__libc_malloc分配一次
本章节主要讲解一下realloc()函数的实现。free函数的入口函数:__libc_realloc,该入口函数也在malloc.c的文件中。
前几章我们讲解了malloc的实现和free的实现,基本了解了ptmalloc是如何来管理内存结构的。此章节主要讲一下realloc()函数。该函数意思就是重置一个内存的大小。基本有以下几个步骤来实现该函数逻辑:

边界情况主要有两种:
通过checked_request2size函数,检查bytes是否在分配合法区间内,并且将bytes进行对齐,最终得到对齐的nb大小的内存申请容量。
- /**
- * 重新设置内存大小
- */
- void *
- __libc_realloc (void *oldmem, size_t bytes)
- {
- mstate ar_ptr;
- INTERNAL_SIZE_T nb; //字节对齐后需要分配的大小 /* padded request size */
-
- void *newp; //返回新的chunk地址 /* chunk to return */
-
- void *(*hook) (void *, size_t, const void *) =
- atomic_forced_read (__realloc_hook);
- if (__builtin_expect (hook != NULL, 0))
- return (*hook)(oldmem, bytes, RETURN_ADDRESS (0));
-
- //如果bytes==0 则相当于free,调用__libc_free函数
- #if REALLOC_ZERO_BYTES_FREES
- if (bytes == 0 && oldmem != NULL)
- {
- __libc_free (oldmem); return 0;
- }
- #endif
-
- /* realloc of null is supposed to be same as malloc */
- /* 如果老的内存指针为空,则相当于重新分配一块内存,调用__libc_malloc函数 */
- if (oldmem == 0)
- return __libc_malloc (bytes);
-
- /* chunk corresponding to oldmem */
- const mchunkptr oldp = mem2chunk (oldmem); //获取老的chunk指针地址
- /* its size */
- const INTERNAL_SIZE_T oldsize = chunksize (oldp); //获取来的chunk的size
-
- /* 判断chunk是否为MMAP分配方式 IS_MMAPPED*/
- if (chunk_is_mmapped (oldp))
- ar_ptr = NULL; //分配区NULL
- else
- {
- MAYBE_INIT_TCACHE ();
- ar_ptr = arena_for_chunk (oldp); //获取分配区
- }
-
- /* Little security check which won't hurt performance: the allocator
- never wrapps around at the end of the address space. Therefore
- we can exclude some size values which might appear here by
- accident or by "design" from some intruder. We need to bypass
- this check for dumped fake mmap chunks from the old main arena
- because the new malloc may provide additional alignment. */
- if ((__builtin_expect ((uintptr_t) oldp > (uintptr_t) -oldsize, 0)
- || __builtin_expect (misaligned_chunk (oldp), 0))
- && !DUMPED_MAIN_ARENA_CHUNK (oldp))
- malloc_printerr ("realloc(): invalid pointer");
-
- //检查bytes是否在合法区间内,最大小于<2147483647
- //并调用request2size函数,将bytes进行对齐,最终得到对齐的nb大小的内存申请容量
- if (!checked_request2size (bytes, &nb)) //XXX
- {
- __set_errno (ENOMEM);
- return NULL;
- }
这里有几个逻辑:
- //如果是MMAP的方式
- if (chunk_is_mmapped (oldp))
- {
- /* If this is a faked mmapped chunk from the dumped main arena,
- always make a copy (and do not free the old chunk). */
- //如果是伪造的主分区的一个MMAP的chunk
- //重新申请一个内存块,并将老的内存块数据拷贝到新的内存块上
- if (DUMPED_MAIN_ARENA_CHUNK (oldp))
- {
- /* Must alloc, copy, free. */
- void *newmem = __libc_malloc (bytes);
- if (newmem == 0)
- return NULL;
- /* Copy as many bytes as are available from the old chunk
- and fit into the new size. NB: The overhead for faked
- mmapped chunks is only SIZE_SZ, not 2 * SIZE_SZ as for
- regular mmapped chunks. */
- if (bytes > oldsize - SIZE_SZ)
- bytes = oldsize - SIZE_SZ;
- memcpy (newmem, oldmem, bytes); //内存拷贝
- return newmem;
- }
-
- void *newmem;
-
- #if HAVE_MREMAP
- newp = mremap_chunk (oldp, nb);
- if (newp)
- return chunk2mem (newp);
- #endif
- /* Note the extra SIZE_SZ overhead. */
- //如果老的内存块的size减去SIZE_SZ后,都比新申请的大,则什么都不做,返回老的指针
- if (oldsize - SIZE_SZ >= nb)
- return oldmem; /* do nothing */
-
- /* Must alloc, copy, free. */
- newmem = __libc_malloc (bytes); //新申请内存
- if (newmem == 0)
- return 0; /* propagate failure */
-
- memcpy (newmem, oldmem, oldsize - 2 * SIZE_SZ); //拷贝oldsize为chunk的大小,2 * SIZE_SZ存储chunk的结构
- munmap_chunk (oldp); //MMAP分配的,调用munmap_chunk所以释放老的内存地址
- return newmem;
- }
单线程情况下直接调用_int_realloc函数。
多线程情况下需要加锁然后调用_int_realloc实现。
- /* 单线程情况 */
- if (SINGLE_THREAD_P)
- {
- newp = _int_realloc (ar_ptr, oldp, oldsize, nb); //直接调用_int_realloc核心函数
- assert (!newp || chunk_is_mmapped (mem2chunk (newp)) ||
- ar_ptr == arena_for_chunk (mem2chunk (newp)));
-
- return newp; //返回新的指针地址
- }
-
- /* 多线程模式下,非MMAP分配方式 */
- __libc_lock_lock (ar_ptr->mutex); //加锁
-
- newp = _int_realloc (ar_ptr, oldp, oldsize, nb); //调用_int_realloc核心函数
-
- __libc_lock_unlock (ar_ptr->mutex); //解锁
- assert (!newp || chunk_is_mmapped (mem2chunk (newp)) ||
- ar_ptr == arena_for_chunk (mem2chunk (newp)));
老的chunk内存,大于新分配的,空间足够,则可以进行分割。将老的内存切割成2个chunk,一个返回给用户端,一个放入bins上进行管理。
oldsize需要大于nb,nb是经过checked_request2size函数进行字节对齐的内存大小。
- /**
- * realloc核心函数
- */
- void*
- _int_realloc(mstate av, mchunkptr oldp, INTERNAL_SIZE_T oldsize,
- INTERNAL_SIZE_T nb)
- {
- mchunkptr newp; /* chunk to return */
- INTERNAL_SIZE_T newsize; /* its size */
- void* newmem; /* corresponding user mem */
-
- mchunkptr next; /* next contiguous chunk after oldp */
-
- mchunkptr remainder; /* extra space at end of newp */
- unsigned long remainder_size; /* its size */
-
- /* oldmem size */
- if (__builtin_expect (chunksize_nomask (oldp) <= 2 * SIZE_SZ, 0)
- || __builtin_expect (oldsize >= av->system_mem, 0))
- malloc_printerr ("realloc(): invalid old size");
-
- check_inuse_chunk (av, oldp); //检查使用状态
-
- /* All callers already filter out mmap'ed chunks. */
- assert (!chunk_is_mmapped (oldp));
-
- next = chunk_at_offset (oldp, oldsize); //获取下一个chunk
- INTERNAL_SIZE_T nextsize = chunksize (next); //下一个chunk的size
- if (__builtin_expect (chunksize_nomask (next) <= 2 * SIZE_SZ, 0)
- || __builtin_expect (nextsize >= av->system_mem, 0))
- malloc_printerr ("realloc(): invalid next size");
-
- //老的chunk内存,大于新分配的,空间足够,则可以进行分割
- if ((unsigned long) (oldsize) >= (unsigned long) (nb))
- {
- /* already big enough; split below */
- newp = oldp;
- newsize = oldsize;
- }
如果 nextchunk 是Topchunk ,并且新的chunk大小大于申请的大小,则直接合并Topchunk进行切割。
调整av->top的值,设置Topchunk 标记物理相邻前一个chunk为使用中PREV_INUSE。
- else
- {
- /* Try to expand forward into top */
- /* 如果 nextchunk 是Topchunk ,并且新的chunk大小大于申请的大小,则直接合并Topchunk进行切割*/
- if (next == av->top &&
- (unsigned long) (newsize = oldsize + nextsize) >=
- (unsigned long) (nb + MINSIZE))
- {
- set_head_size (oldp, nb | (av != &main_arena ? NON_MAIN_ARENA : 0)); //设置头
- av->top = chunk_at_offset (oldp, nb); //调整av->top的值
- set_head (av->top, (newsize - nb) | PREV_INUSE); //设置Topchunk 标记物理相邻前一个chunk为使用中
- check_inuse_chunk (av, oldp);
- return chunk2mem (oldp); //chunk指针地址 转到 内存指针地址
- }
如果nextchunk不是Top chunk ,并且next为空闲状态,则将nextchunk进行合并。合并完了之后,调用unlink_chunk,解除nextchunk的bins上的关系。
- /* Try to expand forward into next chunk; split off remainder below */
- /* 如果nextchunk不是Top chunk ,并且next为空闲状态,则将nextchunk进行合并*/
- else if (next != av->top &&
- !inuse (next) &&
- (unsigned long) (newsize = oldsize + nextsize) >=
- (unsigned long) (nb))
- {
- newp = oldp;
- unlink_chunk (av, next); //解除空闲chunk的bins上的关系
- }
首先调用_int_malloc函数,直接分配一个新的newmem。通过newmem,获取得到当前chunk的大小和chunk地址。如果新分配的正好是nextchunk,则直接合并到一起。如果不是,则释放老内存,拷贝就内存数据到新内存上。
- /* allocate, copy, free */
- /* nextchunk为使用中,或其它情况 */
- else
- {
- newmem = _int_malloc (av, nb - MALLOC_ALIGN_MASK); //调用_int_malloc新分配一块内存
- if (newmem == 0)
- return 0; /* propagate failure */
-
- newp = mem2chunk (newmem); //返回新的指针地址
- newsize = chunksize (newp); //返回新的chunk的size
-
- /*
- Avoid copy if newp is next chunk after oldp.
- */
- /* 如果新分配的物理地址是老chunk的下一个chunk */
- if (newp == next)
- {
- newsize += oldsize; //调整size,合并nextchunk
- newp = oldp;
- }
- else
- {
- memcpy (newmem, chunk2mem (oldp), oldsize - SIZE_SZ); //开始进行内存拷贝
- _int_free (av, oldp, 1); //释放老内存
- check_inuse_chunk (av, newp);
- return chunk2mem (newp); //返回内存地址
- }
- }
- }
老的内存块经过合并后,足够大,则需要进行裁剪操作。如果进行裁剪前,发现没有足够的空间了,则不裁剪了,设置一下nextchunk的标记位就行了;如果有足够空间,则裁剪出Remainder chunk,并通过_int_free函数,将裁剪出来的剩余chunk释放到bins上管理。
- /* If possible, free extra space in old or extended chunk */
-
- assert ((unsigned long) (newsize) >= (unsigned long) (nb));
-
- /* 老的内存块经过合并后,足够大,则需要进行裁剪操作 */
- remainder_size = newsize - nb; //裁剪剩余的内存
-
- /* 进行裁剪前,如果没有足够的空间了,则不裁剪了 */
- if (remainder_size < MINSIZE) /* not enough extra to split off */
- {
- set_head_size (newp, newsize | (av != &main_arena ? NON_MAIN_ARENA : 0));
- set_inuse_bit_at_offset (newp, newsize); //下一个chunk标记前一个chunk使用中
- }
- else /* split remainder */
- {
- remainder = chunk_at_offset (newp, nb); //获取裁剪的chunk
- set_head_size (newp, nb | (av != &main_arena ? NON_MAIN_ARENA : 0));
- set_head (remainder, remainder_size | PREV_INUSE |
- (av != &main_arena ? NON_MAIN_ARENA : 0)); //设置裁剪chunk的状态
- /* Mark remainder as inuse so free() won't complain */
- set_inuse_bit_at_offset (remainder, remainder_size); //
- _int_free (av, remainder, 1); //释放裁剪后的chunk
- }
-
- check_inuse_chunk (av, newp);
- return chunk2mem (newp); //返回内存
- }
如果调用_int_realloc失败,则尝试__libc_malloc,强制重新分配一次。
- /* 分配失败 ,则调用__libc_malloc进行重试一次*/
- if (newp == NULL)
- {
- /* Try harder to allocate memory in other arenas. */
- LIBC_PROBE (memory_realloc_retry, 2, bytes, oldmem);
- newp = __libc_malloc (bytes);
- /* 分配成功,调用memcpy拷贝数据, _int_free释放老的chunk*/
- if (newp != NULL)
- {
- memcpy (newp, oldmem, oldsize - SIZE_SZ);
- _int_free (ar_ptr, oldp, 0);
- }
- }
-
- return newp;
- }
- libc_hidden_def (__libc_realloc)