STL源码分析—空间配置器(续)

来源:互联网 发布:网络弱电箱谁会 编辑:程序博客网 时间:2024/05/01 00:46

http://blog.csdn.net/xietingcandice/article/details/39930603


空闲块列表节点的结构

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. union obj    
  2. {    
  3.     union obj * free_list_link;    
  4.     char client_data[1];    
  5. };   

注意这里用的是union,从第一个字段看是一个指向相同形式的指针,从第二个字段看,是指向实际区块的指针,这样一物两用,节约了空间


分配算法:

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. // 算法:allocate    
  2. // 输入:申请内存的大小size    
  3. // 输出:若分配成功,则返回一个内存的地址,否则返回NULL    
  4. {    
  5.     if(size 大于 128)    
  6.         启动第一级分配器直接调用malloc分配所需的内存并返回内存地址;    
  7.     else    
  8.     {    
  9.         将size向上round up成8的倍数并根据大小从free_list中取对应的表头free_list_head    
  10.         if(free_list_head 不为空)    
  11.         {    
  12.             从该列表中取下第一个空闲块并调整free_list,返回free_list_head    
  13.         }    
  14.         else    
  15.         {    
  16.             调用refill算法建立空闲块列表并返回所需的内存地址    
  17.         }    
  18.     }    
  19. }    
  20.     
  21.     
  22. // 算法:refill    
  23. // 输入:内存块的大小size    
  24. // 输出:建立空闲块链表并返回第一个可用的内存地址    
  25. {    
  26.     调用chunk_alloc算法分配若干个大小为size的连续内存区域并返回起始地址chunk和成功分配的块数nobj    
  27.     if(块数为1)    
  28.         直接返回 chunk;    
  29.     else    
  30.     {    
  31.         开始在chunk地址块中建立free_list    
  32.         根据size取free_list中对应的表头元素free_list_head     
  33.         将free_list_head 指向chunk中偏移起始地址为size的地址处,即free_list_head = (obj*)(chunk+size)    
  34.         再将整个chunk中剩下的nobj-1个内存块串联起来构成一个空闲列表    
  35.         返回chunk,即chunk中第一个空闲的内存块    
  36.     }    
  37. }    
  38.     
  39.     
  40. // 算法:chunk_alloc    
  41. // 输入:内存块的大小size,预分配的内存块数nobj(以引用传递)    
  42. // 输出:一块连续的内存区域的地址和该区域内可以容纳的内存块的块数    
  43. {    
  44.     计算总共所需的内存大小total_bytes    
  45.     if(内存池足以分配,即end_free-start_free >= total_bytes)    
  46.     {    
  47.         则更新start_free    
  48.         返回旧的start_free    
  49.     }    
  50.     else if(内存池不够分配nobj个内存块,但至少可以分配一个)    
  51.     {    
  52.         计算可以分配的内存块数并修改nobj    
  53.         更新start_free并返回原来的start_free    
  54.     }    
  55.     else     // 内存池连一个内存块都分配不了    
  56.     {    
  57.         先将内存池的内存块链入到对应的free_list中后    
  58.         调用malloc操作重新分配内存池,大小为2倍的total_bytes为附加量,start_free指向返回的内存地址    
  59.         if(分配不成功)    
  60.         {    
  61.             if(16个空闲列表中尚有空闲块)    
  62.                 尝试将16个空闲列表中空闲块回收到内存池中再调用chunk_alloc(size,nobj)    
  63.             else    
  64.                 调用第一级分配器尝试out of memory机制是否还有用    
  65.         }    
  66.         更新end_free为start_free+total_bytes,heap_size为2倍的total_bytes    
  67.         调用chunk_alloc(size,nobj)    
  68.     }    
  69. }    
  70.     
  71.     
  72. // 算法:deallocate    
  73. // 输入:需要释放的内存块地址p和大小size    
  74. {    
  75.     if(size 大于128字节)    
  76.         直接调用free(p)释放    
  77.     else    
  78.     {    
  79.         将size向上取8的倍数,并据此获取对应的空闲列表表头指针free_list_head    
  80.         调整free_list_head将p链入空闲列表块中    
  81.     }    
  82. }    

free list取内存块给用户,和将区块回收放入free list的过程

 


多线程环境下内存池互斥访问

    在第二级配置器中,存在着多线程环境的内存池管理,解决多线程环境下内存池互斥访问,需在自由链表free_list中进行修改调整,我们从SGI STL第二级配置器源码中看到,嵌套一个类class _Lock ,该类的作用是解决互斥访问,并且只有两个函数:构造函数和析构函数;使用构造函数对内存池进行加锁,使用析构函数对内存池进行解锁。关于多线程内存池互斥访问的源代码如下:
[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. #ifdef __STL_THREADS  
  2. # include <stl_threads.h>//包含线程文件  
  3. # define __NODE_ALLOCATOR_THREADS true  
  4. # ifdef __STL_SGI_THREADS  
  5.   // We test whether threads are in use before locking.  
  6.   // Perhaps this should be moved into stl_threads.h, but that  
  7.   // probably makes it harder to avoid the procedure call when  
  8.   // it isn't needed.  
  9.     extern "C" {  
  10.       extern int __us_rsthread_malloc;  
  11.     }  
  12.     // The above is copied from malloc.h.  Including <malloc.h>  
  13.     // would be cleaner but fails with certain levels of standard  
  14.     // conformance.  
  15. #   define __NODE_ALLOCATOR_LOCK if (threads && __us_rsthread_malloc) \  
  16.                 { _S_node_allocator_lock._M_acquire_lock(); }  
  17. #   define __NODE_ALLOCATOR_UNLOCK if (threads && __us_rsthread_malloc) \  
  18.                 { _S_node_allocator_lock._M_release_lock(); }  
  19. # else /* !__STL_SGI_THREADS */  
  20. #   define __NODE_ALLOCATOR_LOCK \  
  21.         { if (threads) _S_node_allocator_lock._M_acquire_lock(); }//获取锁  
  22. #   define __NODE_ALLOCATOR_UNLOCK \  
  23.         { if (threads) _S_node_allocator_lock._M_release_lock(); }//释放锁  
  24. # endif  
  25. #else  
  26. //  Thread-unsafe  
  27. #   define __NODE_ALLOCATOR_LOCK  
  28. #   define __NODE_ALLOCATOR_UNLOCK  
  29. #   define __NODE_ALLOCATOR_THREADS false  
  30. #endif  
  31.   
  32. # ifdef __STL_THREADS  
  33.     static _STL_mutex_lock _S_node_allocator_lock;//互斥锁变量  
  34. # endif  
  35.   
  36.     // It would be nice to use _STL_auto_lock here.  But we  
  37.     // don't need the NULL check.  And we do need a test whether  
  38.     // threads have actually been started.  
  39.     class _Lock;  
  40.     friend class _Lock;  
  41.     class _Lock {//解决内存池在多线程环境下的管理  
  42.         public:  
  43.             _Lock() { __NODE_ALLOCATOR_LOCK; }  
  44.             ~_Lock() { __NODE_ALLOCATOR_UNLOCK; }  
  45.     };  
参考:

http://blog.csdn.net/chenhanzhun/article/details/39153797

http://blog.csdn.net/Hackbuteer1/article/details/7724534


0 0