现在,一般来说,我们可以实现malloc使得对malloc的调用将会被映射到系统调用sbrk上,sbrk(n)将会移动程序中断的位置-也就是程序的data段的最后。-偏移n个字节,这意味着,n个字节的内存就被分配给了当前程序
我们最终的实现大概看起来会像这样
void* malloc (unsigned n) { return sbrk(n); }
然而,调用一次sbrk是非常昂贵的。因此,如果我们的malloc实现,通过一次sbrk调用分配到了一块很大的内存块,当需要的时候再把这块内存分成更小的部分,相比不论什么时候需要分配了就去调用malloc,将会更加高效一些。
记住,当申请到的内存已经用完的时候,malloc将不得不调用一次sbrk,此时,新申请到的大块内存将和原来的大块内存不是连续的。
另外,我们将会想要重新使用那些我们已经释放的内存,因此我们的malloc实现应该保持记录当前对程序可用的内存。由于一段时间后,这些可用的小的内存块将不再连续,我们将会使用一个链表保持记录这些可用的内存块。
最后,我们需要记录在我们的链表中,每一小块可用的内存有多大,我们给我们的链表结构添加一个size域
把上面所有的结合起来,最终,这就是一个简单的malloc的实现。
#define MINIMUM 1024 /*通过sbrk分配的最小内存 */ struct header { struct header* next; /* 指向下一个节点的指针 */ unsigned size; } static header base; /* 链表头 */ static header* freep = NULL; /* 空闲内存的链表 */ void* malloc (unsigned n) { header* p, *prev; unsigned nunits; nunits = (n + sizeof(header) - 1) / sizeof(header); /* 检查是否还有有空闲内存的链表 */ if ((prev = freep) == NULL) { base.next = freep = prev = &base; base.size = 0; } for (p = prev->next; ; prev = p, p = p->next) { /* 空间的内存是否足够? */ if (p.size >= nunits) { if (p->size == nunits) /* 如果够? */ prev->next = p->next; else { /* 不够就分配不够的部分 */ p->size -= nunits; p += p->size; p->size = nunits; } freep = prev; return (void *)(p + 1); } if (p == freep) /* wrapped around list */ if ((p = moremem(nunits)) == NULL) return NULL; /* 没有空内存 */ } } /* 从内核中请求更多的内存 */ header* moremem (unsigned n) { char* p; header* up; if (n < MINIMUM) n = MINIMUM; p = sbrk(n * sizeof(header)); if (p == (char *) -1) /* 没有空闲内存 */ return NULL; up = (header *) p; up->size = n; free((void *)(up + 1)); return freep; }
这就是这个函数的要点了,我就不麻烦的实现free(n)了,但是free函数所做的仅仅是把n字节大小的空间插入到链表freep适当的地方就可以了
著作权声明:本文由http://www.cnblogs.com/lazycoding翻译,欢迎转载分享。请尊重作者劳动,转载时保留该声明和作者博客链接,谢谢!