malloc free源码实现

malloc的全称是memory allocation,中文叫动态内存分配,用于申请一块连续的指定大小的内存块区域以void*类型返回分配的内存区域地址,当无法知道内存具体位置的时候,想要绑定真正的内存空间,就需要用到动态的分配内存,且分配的大小就是程序要求的大小。

malloc分配规则

  •  当申请小于128k内存的时候malloc会调用brk()来进行内存的分配
  • 当申请大于128k的内存的时候malloc会调用mmap()来进行内存的分配

这个原因是因为,brk()分配的内存只有当高地址的内存被释放了低地址的才能被释放。而mmap申请的内存可以单独释放。

1. 基础定义

这里主要定义了一个allocation_header结构数组,大小为65536,结构体为记录要分配的实际内存大小和索引。

代码如下:

/* Array of known allocations, to track invalid frees.  */
enum { max_allocations = 65536 };
/*全局数组变量,最大内存分配个数为65536*/
static struct allocation_header *allocations[max_allocations];
/*记录索引*/
static size_t allocation_index;
static size_t deallocation_count;

struct  allocation_header
{
  size_t allocation_index; /*分配索引*/
  size_t allocation_size; /*分配大小*/
};

2. malloc实现

malloc函数内部调用了malloc_internal(), 从实现上看,malloc支持线程安全。
Alt

malloc_internal 记录了当前内存分配信息,包括实际内存分配大小和索引记录。实际内存分配会比用户申请的要多一个sizeof(allocation_header)大小.

malloc_internal 代码如下:

static void *
malloc_internal (size_t size)
{
  if (allocation_index == max_allocations)
  {
    errno = ENOMEM;
    return NULL;
  }
  
  size_t allocation_size = size + sizeof (struct allocation_header);
  if (allocation_size < size)
  {
    errno = ENOMEM;
    return NULL;
  }
  
  size_t index = allocation_index++;
  //实际内存分配
  void *result = mmap (NULL, allocation_size, PROT_READ | PROT_WRITE,
                       MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  if (result == MAP_FAILED)
    return NULL;

  allocations[index] = result;
  *allocations[index] = (struct allocation_header)
    {
      .allocation_index = index,
      .allocation_size = allocation_size
    };
    
  //给用户分配的内存块开始地址,跳过头部地址
  return allocations[index] + 1;
}

3. free实现

我们调用完malloc之后,我们都需要调用free接口才可以,但是我们只需要传入malloc返回的地址即可,大小信息可以从头部信息获取

free源码如下:

void
free (void *ptr)
{
  if (ptr == NULL)
    return;
  lock ();
  //返回内存分配的头部信息,包含了分配的内存大小信息
  //在get_header中对ptr-1,得到实际的内存分配地址。
  struct allocation_header *header = get_header ("free", ptr);
  //完成内存释放工作
  free_internal ("free", header);
  unlock ();
}

get_header 关键代码:
在这里插入图片描述
free_internal 内部实现:
在这里插入图片描述

申请内存块实际分配模型
在这里插入图片描述

以上就是全部glibc简单的内存管理源码分析,每个版本、编译器的内存管理会有差异,但最基本的原理应该是这样。

从操作系统角度来看,进程分配内存有两种方式,分别由两个系统调用完成:brk 和 mmap(不考虑共享内存)。brk 的实现方式是将 Data Segment 的最高地址指针 _edata 往 ...