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支持线程安全。
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简单的内存管理源码分析,每个版本、编译器的内存管理会有差异,但最基本的原理应该是这样。
下一章:malloc 两种实现方式:brk 和 mmap
从操作系统角度来看,进程分配内存有两种方式,分别由两个系统调用完成:brk 和 mmap(不考虑共享内存)。brk 的实现方式是将 Data Segment 的最高地址指针 _edata 往 ...