您的位置:首页 > 其它

内存管理学习笔记——自定义malloc()与内存池的实现(未完)

2016-03-12 00:04 411 查看
最近在巩固算法,这个先做笔记了,有空再精细研究。

知道可以动态申请堆空间,堆空间是系统维护的内存块链表,用户申请时,系统找到合适大小的堆给用户,系统还会把剩下的空间再切割备用,所以会出碎片。

blabla。。。。。。

一直以来困惑一个问题。系统怎么知道那一块大小,送一个指针过去它就释放和malloc对应的空间?大小什么的都记录在哪?

看了一个自实现,想明白一些了:

struct mem_control_block {
int is_available;
int size;
};

这些内存块,是有内存控制块的,是否可用,应该就是分配和释放时候用的。分配的时候,查查是否可用,分配成功之后改成不可用,释放的时候,把标记再改为不可用。另外一个size就是分配空间的大小了,这样的话释放空间的时候就知道释放多大了,相应的,这个也应该是分配的时候赋值的,因为用户指定大小。

然后就困惑一个问题,怎么映射?这个结构体,怎么映射到对应空间上?没个指针什么的?

第一反应是协议包的封装,我们封装协议包时,本就是一块连续空间,把包头放在前边,包的内容放在后边而已。其实仔细想想,协议包又何尝不是叫做协议控制块呢,和内存控制块有什么区别么?

所以这样就说通了,真实的内存块可能是如下模样:

+----------+---------------------------------------------------+

| MCB   | MEM_FOR_USER                         |

+----------+---------------------------------------------------+

^           ^

|            |

sys        user

有系统和用户两个指针,给用户第二个指针,就隐藏了内部实现了,然后系统指针在前边就能看到内存控制块。

你猜怎着?下文真是这样说的,这样你free()回传一个指针的时候,我系统直接回滚一个控制块的大小就好了。所以学东西要多思考,多疑问,你想的,可能就是正确答案,这样还能加深印象。

不过,问题也来了,如果真是按这种结构来存,那如果用户自己乱改指针,改到前边的MCB,不就糟了么?也就是假设这个MCB并不是对用户不可见的,只是没给用户直接访问途径。这样一来,一个误操作就能导致最后内存的释放不正确!可能释放少,也可能释放多。前者可能会导致内存”丢失“?,后者可能会导致地址”重合“?这会不会出什么大乱子?!系统有没有什么容错机制?(其实这篇帖子就是自己实现malloc的,看看他有什么高招)

先做个基于原生malloc()的破坏性试验试试?没想好怎么测试,因为释放的内存也不是把内容全都置零,指针传一次给free()再传一次也不会提示错,所以怎么验证释放成功与失败也是个问题。

或者强行提取内存内容,看看有没有数值和申请的内存大小一致。

void*指针不让提取,用个int*指针试试看



上图可以看到,往前退一个int地址(4字节)后,打印出了105(再往前或者往后都是0),不知是不是巧合100+5?,100个地址给用户,5个地址存状态?当然,要指出一点,上文给出的实现,其实浪费空间了,想知道一个内存块是否分配,是不需要一个int型的,一个char型足矣,一个char型加一个int型的话,就刚好是5地址。

malloc()换个大小的再试试:

分配50大小,打印出的前两格的值是57,多7个,所以上边的假设也不成立。

分配10大小,打印17

分配200,打印209

毫无规律,当然也不排除提取地址不对的可能性,但是整体数量级差距不大,只是具体数值是多几的问题上,所以更像是细节上的问题。

另外一个问题也先提出来吧,对这个疑问可能有些帮助,分配给你的空间,一定是准确的你指定的大小么?多余的都完整截去当碎片重新分配了?或者分配出来本来就有零头?

找到这样一句话(但这说的是sbrk(),而自己的实现没处理,不确定原生malloc()也是这样不做处理。):

由于 
sbrk()
 可能会交回比我们请求的更多的内存,所以在堆(heap)的末端
会遗漏一些内存。

另外,free()操作时:

传array释放之后,指向前4字节位置的ptr的内容也从近似内存片段大小片成一个野数,比如135169。

前8字节位置的0还是0,没变化。

另外一个问题就是:所谓系统维护一个”链表“,通过链表来找到一个又一个的内存块,那这个链表什么样?其实应该一个非常简单的链表就好了,因为所需的信息被储存在内存块的前半部分了,不用在链表结构多下工夫。

anyway,不在这个细节上浪费时间了,再看看那个malloc实现。

=========================================================================================================================

所以,这就是自实现的free()函数

void free(void *firstbyte) {
struct mem_control_block *mcb;
/* Backup from the given pointer to find the
* mem_control_block
*/
mcb = firstbyte - sizeof(struct mem_control_block);
/* Mark the block as being available */
mcb->is_available = 1;
/* That's It!  We're done. */
return;
}

回退一个结构体,赋值指针,然后给成员is_available设置成可用,这里并没有去管那个size,和上边推测的free()有出入,前提是上边的推测是正确的,不过也可能实现不太一样,位置不一样(不过再不一样也不能那么巧合的只在数值差几了~)。

malloc的实现例子(缺点多多,只是一个参考):

void *malloc(long numbytes) {
/* Holds where we are looking in memory */
void *current_location;
/* This is the same as current_location, but cast to a
* memory_control_block
*/
struct mem_control_block *current_location_mcb;
/* This is the memory location we will return.  It will
* be set to 0 until we find something suitable
*/
void *memory_location;
/* Initialize if we haven't already done so */
if(! has_initialized) 	{
malloc_init();
}
/* The memory we search for has to include the memory
* control block, but the users of malloc don't need
* to know this, so we'll just add it in for them.
*/
numbytes = numbytes + sizeof(struct mem_control_block);
/* Set memory_location to 0 until we find a suitable
* location
*/
memory_location = 0;
/* Begin searching at the start of managed memory */
current_location = managed_memory_start;
/* Keep going until we have searched all allocated space */
while(current_location != last_valid_address)
{
/* current_location and current_location_mcb point
* to the same address.  However, current_location_mcb
* is of the correct type, so we can use it as a struct.
* current_location is a void pointer so we can use it
* to calculate addresses.
*/
current_location_mcb =
(struct mem_control_block *)current_location;
if(current_location_mcb->is_available)
{
if(current_location_mcb->size >= numbytes)
{
/* Woohoo!  We've found an open,
* appropriately-size location.
*/
/* It is no longer available */
current_location_mcb->is_available = 0;
/* We own it */
memory_location = current_location;
/* Leave the loop */
break;
}
}
/* If we made it here, it's because the Current memory
* block not suitable; move to the next one
*/
current_location = current_location +
current_location_mcb->size;
}
/* If we still don't have a valid location, we'll
* have to ask the operating system for more memory
*/
if(! memory_location)
{
/* Move the program break numbytes further */
sbrk(numbytes);
/* The new memory will be where the last valid
* address left off
*/
memory_location = last_valid_address;
/* We'll move the last valid address forward
* numbytes
*/
last_valid_address = last_valid_address + numbytes;
/* We need to initialize the mem_control_block */
current_location_mcb = memory_location;
current_location_mcb->is_available = 0;
current_location_mcb->size = numbytes;
}
/* Now, no matter what (well, except for error conditions),
* memory_location has the address of the memory, including
* the mem_control_block
*/
/* Move the pointer past the mem_control_block */
memory_location = memory_location + sizeof(struct mem_control_block);
/* Return the pointer */
return memory_location;
}

这是原文:https://www.ibm.com/developerworks/cn/linux/l-memory/index.html

小结:

这些东西各有优缺点,是根据使用场景进行的优化选择,有Doug Lea Malloc、BSD
Malloc与Hoard等。

直接重写malloc(),也算是一种实现内存管理的方式。

malloc()这种形式管理内存会遇到很多麻烦,比如到底是申请者还是使用者来释放,生存期太长,控制不好会不会爆。

===========================================================================================================

内存池:

内存池可以分阶段划分,多个阶段用多个不同的池子,过了阶段直接抹掉。还可以注册清除函数来完成析构。

后边的眼晕,先不看了

另外,看一个例子,malloc8个字节,使用时候不小心超了,然后再free就会错。
http://www.cnblogs.com/chuyuhuashi/p/4480117.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: