malloc过程中做了那些?

malloc()函数的工作机制

       malloc函数的实质体现在,它有一个将可用的内存块连接为一个长长的列表的所谓空闲链表。调用malloc函数时,它沿连接表寻找一个大到足以满足用户请求所需要的内存块。然后,将该内存块一分为二(一块的大小与用户请求的大小相等,另一块的大小就是剩下的字节)。接下来,将分配给用户的那块内存传给用户,并将剩下的那块(如果有的话)返回到连接表上。调用free函数时,它将用户释放的内存块连接到空闲链上。到最后,空闲链会被切成很多的小内存片段,如果这时用户申请一个大的内存片段,那么空闲链上可能没有可以满足用户要求的片段了。于是,malloc函数请求延时,并开始在空闲链上翻箱倒柜地检查各内存片段,对它们进行整理,将相邻的小空闲块合并成较大的内存块。

           申请的时候实际上占用的内存要比申请的大。因为超出的空间是用来记录对这块内存的管理信息
       大多数实现所分配的存储空间比所要求的要稍大一些,额外的空间用来记录管理信息——分配块的长度,指向下一个分配块的指针等等。这就意味着如果写过一个已分配区的尾端,则会改写后一块的管理信息。这种类型的错误是灾难性的,但是因为这种错误不会很快就暴露出来,所以也就很难发现。将指向分配块的指针向后移动也可能会改写本块的管理信息。
记录管理信息的数据结构

[cpp]  view plain copy
  1. struct mem_control_block {  
  2. int is_available;  
  3. int size;  
  4. };  

在大部分操作系统中,内存分配由以下两个简单的函数来处理:
void *malloc (long numbytes):该函数负责分配 numbytes大小的内存,并返回指向第一个字节的指针。
void free(void *firstbyte):如果给定一个由先前的 malloc返回的指针,那么该函数会将分配的空间归还给进程的“空闲空间”。
malloc_init将是初始化内存分配程序的函数。它要完成以下三件事:将分配程序标识为已经初始化,找到系统中最后一个有效内存地址,然后建立起指向我们管理的内存的指针。这三个变量都是全局变量:


1. 分配程序的全局变量
int has_initialized = 0;
void *managed_memory_start;
void *last_valid_address;
如前所述,被映射的内存的边界(最后一个有效地址)常被称为系统中断点或者 当前中断点。在很多 UNIX系统中,为了指出当前系统中断点,必须使用 sbrk(0) 函数。 sbrk根据参数中给出的字节数移动当前系统中断点,然后返回新的系统中断点。使用参数 0 只是返回当前中断点。 

2. 分配程序初始化函数
[cpp]  view plain copy
  1. void malloc_init()  
  2. {  
  3.   
  4. last_valid_address = sbrk(0);  
  5.   
  6. managed_memory_start = last_valid_address;  
  7.   
  8. has_initialized = 1;  
  9. }  

3.记录管理信息的数据结构
[cpp]  view plain copy
  1. struct mem_control_block {  
  2. int is_available;  
  3. int size;  
  4. };  

4. 解除分配函数
[cpp]  view plain copy
  1. void free(void *firstbyte) {  
  2. struct mem_control_block *mcb;  
  3.   
  4. mcb = firstbyte - sizeof(struct mem_control_block);  
  5.   
  6. mcb->is_available = 1;  
  7.   
  8. return;  
  9. }  
5. 主分配程序 
[cpp]  view plain copy
  1. void *malloc(long numbytes) {  
  2.      
  3.     void *current_location;  
  4.      
  5.     struct mem_control_block *current_location_mcb;  
  6.      
  7.     void *memory_location;  
  8.      
  9.     if(! has_initialized) {  
  10.         malloc_init();  
  11.     }  
  12.      
  13.     numbytes = numbytes + sizeof(struct mem_control_block);  
  14.      
  15.     memory_location = 0;  
  16.      
  17.     current_location = managed_memory_start;  
  18.      
  19.     while(current_location != last_valid_address)  
  20.     {  
  21.          
  22.         current_location_mcb =  
  23.             (struct mem_control_block *)current_location;  
  24.         if(current_location_mcb->is_available)  
  25.         {  
  26.             if(current_location_mcb->size >= numbytes)  
  27.             {  
  28.                  
  29.                  
  30.                 current_location_mcb->is_available = 0;  
  31.                  
  32.                 memory_location = current_location;  
  33.                  
  34.                 break;  
  35.             }  
  36.         }  
  37.          
  38.         current_location = current_location +  
  39.             current_location_mcb->size;  
  40.     }  
  41.      
  42.     if(! memory_location)  
  43.     {  
  44.          
  45.         sbrk(numbytes);  
  46.          
  47.         memory_location = last_valid_address;  
  48.          
  49.         last_valid_address = last_valid_address + numbytes;  
  50.          
  51.         current_location_mcb = memory_location;  
  52.         current_location_mcb->is_available = 0;  
  53.         current_location_mcb->size = numbytes;  
  54.     }  
  55.      
  56.      
  57.     memory_location = memory_location + sizeof(struct mem_control_block);  
  58.      
  59.     return memory_location;  
  60. }  


Linux内核中采用了一种同时适用于32位和64位系统的内存分页模型,对于32位系统来说,两级 页表足够用了,而在x86_64系统中,用到了四级页表,如图2-1所示。四级 页表分别为:
页全局目录(Page Global Directory)
页上级目录(Page Upper Directory)
页中间目录(Page Middle Directory)
页表(Page Table)
 页全局目录包含若干页上级目录的地址,页上级目录又依次包含若干页中间目录的地址,而页中间目录又包含若干 页表的地址,每一个页表项指向一个 页框。Linux中采用4KB大小的 页框作为标准的 内存分配单元。
在实际应用中,经常需要分配一组连续的 页框,而频繁地申请和释放不同大小的连续页框,必然导致在已分配页框的内存块中分散了许多小块的空闲页框。这样,即使这些 页框是空闲的,其他需要分配连续页框的应用也很难得到满足。
为了避免出现这种情况,Linux内核中引入了伙伴系统算法(buddy system)。把所有的空闲 页框分组为11个块链表,每个块链表分别包含大小为1,2,4,8,16,32,64,128,256,512和1024个连续页框的页框块。最大可以申请1024个连续 页框,对应4MB大小的连续内存。每个 页框块的第一个页框的物理地址是该块大小的整数倍。
假设要申请一个256个 页框的块,先从256个页框的链表中查找空闲块,如果没有,就去512个 页框的链表中找,找到了则将页框块分为2个256个页框的块,一个分配给应用,另外一个移到256个页框的链表中。如果512个页框的链表中仍没有空闲块,继续向1024个页框的链表查找,如果仍然没有,则返回错误。
页框块在释放时,会主动将两个连续的页框块合并为一个较大的页框块。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值