前言
本篇博客利用C标准库现有的malloc
和free
函数,在其基础之上编写一个更强大的动态内存分配器,它可以实现出错预警的功能。
具体的出错预警功能描述,参见 SSD6 Exercise3——Debugging Malloc Lab: Detecting Memory-Related 的题目要求。
基础知识
背景
虽然可以使用低级的
mmap
和munmap
函数来创建和删除虚拟内存的区域,但是C程序员还是会觉得当运行时需要额外虚拟内存时,用动态内存分配器 (dynamic memory allocator) 更方便,也有更好的可移植性。
……
显式分配器 (explicit allocator) ,要求应用显式地释放任何已分配的块。例如,C标准库提供一种叫做mallo
c程序包的显式分配器,并通过调用free
函数来释放一个块。C++中的new
和delete
操作符和C中的malloc
和free
相当。
本篇博客就利用C标准库提供的malloc
和free
函数,为其编写一个更强大的动态内存分配器,它可以实现出错预警的功能。
下面我们先来看C标准库中malloc
与free
分别为我们提供了哪些功能。
malloc
#include <stdlib.h>
void *malloc(size_t size);
malloc
的参数就是需要分配的内存字节数,如果内存池中的可用内存可以满足这个需求,函数就返回一个指向被分配的内存块起始位置的指针。
free
#include <stdlib.h>
void free(void *pointer);
free
的参数要么是NULL
,要么是一个之前从malloc
、calloc
或realloc
函数得到的返回值。向free
传递一个NULL
参数不会产生任何效果。
缺陷分析
在动态内存分析中,常常会出现许多错误,这些错误可能包括:
- 对NULL指针进行解引用操作
- 对分配的内存进行操作时越过边界
- 释放并非动态分配的内存
- 试图释放一块动态分配内存的一部分
- 一块动态内存被释放之后被继续使用
而正如我们所看到的,C标准库中原有的malloc
、calloc
、realloc
与free
函数,并不会对这些可能出现的错误进行相应的预警处理。
因此我们试图通过编写一个Enhanced Allocator,来解决这一缺陷。
方案设计
数据抽象
我们对一个连续的虚拟内存片 (chunk) 进行下图所示的设计:
- 每个
chunk
由Header
、Payload
、Footer
这三部分组成。 Header
至少包括如下内容:
checkSum (int)
:一个chunk
最前面的部分,通过检查checkSum
的变化,可判断出Header
有没有被错误更改。size (size_t)
:Payload
的大小filename (char *)
:文件名linenum (int)
:行号
Fence
:为特定的常数值。在一个chunk
的Header
与Footer
中各有一个Fence
,通过检查其值的变化,可判断出Payload
的前方与后方有没有被错误更改。Payload
:原应分配的内存块。Footer
:由一个Fence
组成。
错误处理
我们所完成的Enhanced Allocator,拟对如下5种错误进行捕获处理:
Error #1: Writing past the beginning of the user’s block (through the fence)
Error #2: Writing past the end of the user’s block (through the fence)
Error #3: Corrupting the header information
Error #4: Attempting to free an unallocated or already-freed block
Error #5: Memory leak detection (user can use ALLOCATEDSIZE to check for leaks at the end of the program)
数据结构
下面我们来看具体的数据结构如何实现。
我们可以看到,如果想要对上述的5种错误进行捕获,我们需要维护(记录)当前进程中所有已经分配的内存片,从而在释放内存时,判断出free
的参数是否有效。
那么我们就可以采用最简单,也是最直接的方式,就通过一个单向链表实现对所有已经分配的内存片的记录。
/* Define Header */
struct header{
int checkSum;
size_t size;
char *filename;