内存池技术(三)——近观内存池

      今天进入主要函数的学习。首先看用户申请内存函数mem_malloc:

Code:
  1. static void *mem_malloc(size_t n)   
  2. {   
  3.     obj * volatile *my_free_list;   
  4.     obj *result;   
  5.   
  6.     /* 如果申请的内存超过我们设置的最大内存,直接使用crt */  
  7.     if (n > (size_t)__MAX_BYTES) {   
  8.         void *p = (void *)malloc(n);   
  9.         return p;   
  10.     }
  11.     /* 寻找16个free list链表中适当的一个 */  
  12.     my_free_list = free_list + __freelist_index(n);   
  13.     result = *my_free_list;   
  14.     if (result == 0) {   
  15.         /* 当没有可用的free list时,准备重新填充free list */  
  16.         void *ret = refill(__round_up(n));   
  17.         return ret;   
  18.     }   
  19.   
  20.     /* 调整链表 */  
  21.     *my_free_list = result->free_list_link;   
  22.     return result;   
  23. }  

      这里需要说明的是在开始的时候,为了提高效率,内存分配及填充free list会推迟到用户申请内存的时候,其他没什么说的。接着看释放内存函数mem_free:

Code:
  1. static void mem_free(void *p, size_t n)   
  2. {   
  3.     obj *q = (obj*)p;   
  4.     obj * volatile *my_free_list;   
  5.   
  6.     /* 大于__MAX_BYTES直接释放 */  
  7.     if (n > (size_t) __MAX_BYTES) {  
  8.         free(p);
  9.         return;
  10.     }
  11.        
  12.     /* 找到适当的链表,回收区块并调整链表 */    
  13.     my_free_list = free_list + __freelist_index(n);   
  14.     q->free_list_link = *my_free_list;   
  15.     *my_free_list = q;   
  16. }  

      释放过程也没什么可说的,接着看填充链表函数refill:

Code:
  1. static void *refill(size_t n)   
  2. {   
  3.     int nobjs = 20;   
  4.     char *chunk = chunk_alloc(n, &nobjs);   
  5.     obj * volatile *my_free_list;   
  6.     obj *result;   
  7.     obj *current_obj, *next_obj;   
  8.     int i;   
  9.   
  10.     if (nobjs == 1)   
  11.         return chunk;   
  12.        
  13.     my_free_list = free_list + __freelist_index(n);   
  14.   
  15.     result = (obj *)chunk;/* 这个区块返回给用户 */  
  16.   
  17.     /* free list指向新配置的空间(取自内存池)*/  
  18.     *my_free_list = next_obj = (obj *)(chunk + n);   
  19.   
  20.     /* 以下将各个节点串接起来 */  
  21.     for (i = 1; ; i++) {/* 从1开始,因为第0块返回给用户 */  
  22.         current_obj = next_obj;   
  23.         next_obj = (obj *)((char *)next_obj + n);   
  24.            
  25.         if (nobjs - 1 == i) {/* 最后一个节点*/  
  26.             current_obj->free_list_link = NULL;   
  27.             break;   
  28.         } else {   
  29.             current_obj->free_list_link = next_obj;   
  30.         }   
  31.     }   
  32.     return result;   
  33. }  

     当用户调用申请mem_malloc发现找到的对应链表指针指向的NULL时,将会调用refill,refill会调用chunk_alloc从内存池‘要’一定数量的用户所申请区块大小的内存。如果此时内存池返回1个区块,那么直接返回给用户,完成申请过程,否者将除去返给用户的区块外,剩下内存串接起来以备下次使用。接下来看内存池函数chunk_alloc,她的工作是从内存池中取空间给free list用:

Code:
  1.   
  2. static char *chunk_alloc(size_t size, int *nobjs)      
  3. {      
  4.     char *result;      
  5.     size_t total_bytes = size * (*nobjs);        /* 要申请的内存总量 */     
  6.     size_t byte_left = end_free - start_free; /* 内存池剩余空间 */     
  7.      
  8.     if (byte_left >= total_bytes) { /* 内存池中的内存完全满足需要 */     
  9.         result = start_free;      
  10.         start_free += total_bytes;      
  11.         return result;      
  12.     } else if (byte_left >= size) { /* 内存池不能完全满足需求,但能提供一块以上的区块 */     
  13.         *nobjs = byte_left / size;      
  14.         total_bytes = size * (*nobjs);      
  15.         result = start_free;      
  16.         start_free += total_bytes;      
  17.         return result;      
  18.     } else {      
  19.         /* 内存池剩余空间连一个区块的大小都无法提供 */     
  20.         size_t bytes_to_get = + __round_up(heap_size >> 4);      
  21.                                                 
  22.         /* 将内存池的零头配给适当的free list */     
  23.         if (byte_left > 0) {      
  24.             obj * volatile * my_free_list = free_list + __freelist_index(byte_left);      
  25.             ((obj*)start_free)->free_list_link = *my_free_list;      
  26.             *my_free_list = (obj *)start_free;      
  27.         }      
  28.               
  29.         /* 配置heap空间,用来补充内存池 */     
  30.         start_free = (char *)malloc(bytes_to_get);      
  31.         if (0 == start_free) { /* heap 空间不足,malloc()失败 */     
  32.             size_t i;      
  33.             obj * volatile *my_free_list;      
  34.             obj *p;      
  35.                                                                     
  36.             /* 搜寻适当的(比当前size大)free list */     
  37.             for (i = size; i <= __MAX_BYTES; i += __ALIGN) {      
  38.                 my_free_list = free_list + __freelist_index(i);      
  39.                 p = *my_free_list;       
  40.                 if (0 != p) { /* free list 内尚有未用区块 */  
  41.                     /* 调整free list以释放出未用区块 */  
  42.                     *my_free_list = p->free_list_link;      
  43.                     start_free = (char *)p;      
  44.                     end_free = start_free + i;      
  45.                     return chunk_alloc(size, nobjs); /* 递归调用,为了修正nobjs */  
  46.                    /* ps:任何残余零头最终会编入适当free list中备用 */  
  47.                 }      
  48.             }   
  49.             end_free = 0;      
  50.             start_free = (char*)malloc(bytes_to_get);         
  51.         }      
  52.         /* 记录当前内存容量 */  
  53.         heap_size += bytes_to_get;   
  54.         end_free = start_free + bytes_to_get;   
  55.         return chunk_alloc(size,nobjs);   
  56.     }      
  57. }   

      chunk_alloc的作用是从内存池中取内存给free list,所以会竭尽全力满足用户需要。如果有足够的空间,会返回指定数量的区块,否者返回实际能提供数量的区块,或者无法满足用户需要,那么会将内存池剩下的零头编入适当的链表中,然后向系统索要空间来补充内存池。如果系统也无内存可用,则寻找free list中是否有比size大的区块,将其用来补充空间。
      如果到处都已无内存可用了,会再次配置空间,可能抛出异常。否则记录内存池大小。接下来我们看内存池的使用。

 补上mem_realloc函数:

Code:
  1. static void *mem_realloc(void *ptr, size_t new_sz, size_t old_sz)   
  2. {   
  3.     void *result;   
  4.     size_t copy_sz;   
  5.   
  6.     /* 如果old_sz 和 new_sz均大于我们设置的最大内存,那么直接用 crt */  
  7.     if (old_sz > (size_t)__MAX_BYTES && new_sz > (size_t)__MAX_BYTES)   
  8.         return realloc(ptr, new_sz);   
  9.        
  10.     if (__round_up(old_sz) == __round_up(new_sz))   
  11.         return ptr;   
  12.     result = mem_malloc(new_sz);   
  13.     copy_sz = new_sz > old_sz ? old_sz : new_sz;   
  14.     memcpy(result, ptr, copy_sz);   
  15.     mem_free(ptr, old_sz);   
  16.     return result;   
  17. }  

      mem_realloc的作用是替换是替换realloc函数,这里要结合上下文来理解,如果old_sz 和 new_sz均大于我们设置的最大内存,那么直接用 crt。那么假设old_sz < __MAX_BYTES = 128呢?这里分两种情况考虑:
      1、new_sz > __MAX_BYTES:显然内存池不能满足需要,可为什么程序还要执行这句:result = mem_malloc(new_sz)呢,这里别忘了mem_malloc对超过__MAX_BYTES的内存会使用crt
      2、new_sz <= __MAX_BYTES:
            a、__round_up(old_sz) == __round_up(new_sz):直接返回ptr。这里可能存在的一点疑惑的是,假设old_sz = 1, new_sz = 5,那么直接返回的话不是没有满足用户需要吗?其实不然,原因是我们使用mem_malloc(old_sz)时分配的内存是8啊!
            b、__round_up(old_sz) != __round_up(new_sz):这种情况直接执行后续操作——分配内存,将原有内容复制到新内存,释放旧内存。
      如果 old_sz > __MAX_BYTES 且 new_sz < __MAX_BYTES呢?很简单,如2.b一样,直接执行后续操作。
      能说的就这么一点了。

你可能感兴趣的:(list,byte,up,null,工作)