我跟着深入理解计算机系统,自己实现了一遍malloc这个实验,书中要求自己实现一个动态内存分配器,malloc,free,realloc;
我只是简单的把malloc实现了,realloc并没有仔细的去做;
最终实验结果将根据空间利用率和吞吐量进行判断;
书中介绍了几种动态内存管理分配的策略:
- 隐式空闲链表,书中将一个每次分配的块,设置成有固定大小的4字节头节点和4字节尾节点,然后根据每个块的size,移动到下一个块,从而这里是连续的移动,并且用一种头尾节点来连接前后块,每次查找时,都是从哨兵开始,如果是已分配的则将标记位设为1,在查找过程中是整个块的线性长度,此为隐式链表
- 显式空闲链表,在堆中显示的维护一个空闲链表块,每个块都有其头部,其中存储了它的size,如果需要一块内存,则直接在空闲链表块中查找看是否有合适的,如果有,则将此块从链表中删除,然后修复链表,这样的查找时间就是整个空闲链表的线性长度,此为显示链表
- 分离空闲链表,专门维护多个链表,其大小分别是8的倍数;8,16,32,64,128…… 4096,然后将请求的size变成8的倍数,计算它应该所在的链表,然后看此链表中是否还有空闲块,如果没有,则重新申请一块,将剩下的编入到对应的空闲链表中;
刚开始,我还有个疑问,我如果给要请求的size分配内存,那我在写这个函数中用到的变量的内存应该放在哪呢?
因为malloc是用户请求在堆中申请内存,然后自行实行释放(free),而我们在写这个函数的时候,要么是函数里的局部变量,其是存储在栈区的,等进程结束,由系统自动释放,还有就是设置的全局变量,其是放在静态区的,还有申请的static 静态变量,其也是放在静态区,但这里的静态变量,一个是全局静态变量,其在这个源文件都是有效的,但是如果是多个源文件,就不会在其他源文件有效,但全局变量在其他源文件还是有效的,还有就是函数里面的静态变量,此只在该函数里面是有效的,但是只需要初始化一次;(回过头来看了一下,我用了好多其啊。。。)
好了,我接下来,就从整个malloc的整个运行流程开始讲解:
首先是mem_init函数,其是模拟堆的内存,也就是接下来所有分配的块都会在这个MAX_HEAP里面,如果超出堆的size就会返回null
void mem_init(void)
{
/* allocate the storage we will use to model the available VM */
if ((mem_start_brk = (char *)malloc(MAX_HEAP)) == NULL) {
fprintf(stderr, "mem_init_vm: malloc error\n");
exit(1);
}
mem_max_addr = mem_start_brk + MAX_HEAP; /* max legal heap address */
mem_brk = mem_start_brk; /* heap is empty initially */
}
接下来堆的内存有了;
接下来定义malloc所需要的一些函数和宏命令;
首先一块大小的数据结构是这样的:
首先在每一个块头部,有一个四字节大小的头块,其中前29位存储了该块的大小,然后后三位存储了是否已分配,这个已分配位在隐式空闲链表中是非常有用的,因为你只有该块的地址,但你并不知道该块是否已分配,所以要在这里加一个判断位,但是在显示空闲链表中就不需要,因为这些空闲块是在空闲链表中维护的ÿ