Boost学习笔记之六 深度剖析pool

很久没看boost源码了,今天又拿来看了看,这次看了boost中一个内存池的实现,感觉很不错,但似乎里面重复代码多了一点: )

先来看一个辅助结构,这个辅助结构用模板元编程实现获得最小公倍数和最大公约数

求最大公约数时用的是欧几里得算法(这里为了让这个模板更容易看懂,做了一些简化工作).

template <unsigned A, unsigned B, bool Bis0>

struct ct_gcd_helper;

 

template <unsigned A, unsigned B>

struct ct_gcd_helper<A, B, false>

{

  static const unsigned  A_mod_B_ = A % B;

  static const unsigned value = ct_gcd_helper<B, static_cast<unsigned>(A_mod_B_),

        (A_mod_B_ == 0)>::value;

};

 

template <unsigned A, unsigned B>

struct ct_gcd_helper<A, B, true>

{

  static const unsigned value = A;

};

这里用来模板偏特化,如果条件为真,也即A_mod_B == 0,value = A,否则一直这样取模下去

 

//计算最大公约数

template <unsigned A, unsigned B>

struct ct_gcd

{

  BOOST_STATIC_ASSERT(A != 0 && B != 0);

  static const unsigned value = ct_gcd_helper<A, B, false>::value;

};

 

//计算最小公倍数

template <unsigned A, unsigned B>

struct ct_lcm

{

  static const unsigned value =

      A / ct_gcd<A, B>::value * B);

   //valueA除以最大公约数乘以B

};

 

此外还提供了两个用迭代实现得求最小公倍数和最大公约数的函数

template <typename Integer>

Integer gcd(Integer A, Integer B)

{

  do

  {

    const Integer tmp(B);

    B = A % B;

    A = tmp;

  } while (B != 0);

 

  return A;

}

 

template <typename Integer>

Integer lcm(const Integer & A, const Integer & B)

{

  Integer ret = A;

  ret /= gcd(A, B);

  ret *= B;

  return ret;

}

 

在看下一个辅助结构之前,看看这个结构处理的内存块的形式

   http://hi.baidu.com/lyskyly/album/item/5c4a2cf35d4fe951342acc70.html

                            图一

这是pool过一会儿分配的内存块,上面蓝色小格为请求的内存块大小经过适当处理后的大小,这样的pool构造时小块默认是32,以后每分配一个大块内存,就让小块内存加倍,你可以在pool构造函数的第二个参数指定.最后划分出两个小块,第一个小块用来存放下一个这大内存块的指针,这样这些大内存块就可以串起来形成一个链表.

第二个内存块存放下一个块的大小.

然后看这个辅助结构

template <typename SizeType>

class PODptr

{

  public:

    typedef SizeType size_type;

 

  private:

    char * ptr;

    size_type sz;

 

    char * ptr_next_size() const

    { return (ptr + sz - sizeof(size_type)); }

    char * ptr_next_ptr() const

    {

      return (ptr_next_size() -

          pool::ct_lcm<sizeof(size_type), sizeof(void *)>::value);

    }

 

  public:

    PODptr(char * const nptr, const size_type nsize)

    :ptr(nptr), sz(nsize) { }

    PODptr()

    :ptr(0), sz(0) { }

 

    bool valid() const { return (begin() != 0); }

    void invalidate() { begin() = 0; }

    char * & begin() { return ptr; }

    char * begin() const { return ptr; }

    char * end() const { return ptr_next_ptr(); }

    size_type total_size() const { return sz; }

    size_type element_size() const

    {

      return (sz - sizeof(size_type) -

          pool::ct_lcm<sizeof(size_type), sizeof(void *)>::value);

    }

 

    size_type & next_size() const

    { return *(reinterpret_cast<size_type *>(ptr_next_size())); }

    char * & next_ptr() const

    { return *(reinterpret_cast<char **>(ptr_next_ptr())); }

 

    PODptr next() const

    { return PODptr<size_type>(next_ptr(), next_size()); }

    void next(const PODptr & arg) const

    {

      next_ptr() = arg.begin();

      next_size() = arg.total_size();

    }

};

在构造函数中初始化了ptrsz,ptr是指向这块内存头的一个char*型指针,sz是这块内存的总大小.

    char * ptr_next_size() const

    { return (ptr + sz - sizeof(size_type)); }

这个成员函数是用来定位存放下一个块的大小的那个小块的(第二个小块),它让ptr + sz这样移动到大块的末尾,然后再减去sizeof(size_type),这是这个小块的大小,这样就定位到这个小块了.

同理

    char * ptr_next_ptr() const

    {

      return (ptr_next_size() -

          pool::ct_lcm<sizeof(size_type), sizeof(void *)>::value);

}

用来定位到指向下一个大块的指针那个小块.(第一个白色小块)

 

    char * & begin() { return ptr; }

    char * begin() const { return ptr; }

begin()用来返回ptr的值或它的引用

 

bool valid() const { return (begin() != 0); }

返回ptr是否为0,如果不为0,就是可用的.

 

void invalidate() { begin() = 0; }

ptr = 0,让这这个对象不可用, begin() = 0;这种形式是因为此时返回的是ptr的引用

 

char * end() const { return ptr_next_ptr(); }

返回指向下一个大块的指针

 

    size_type element_size() const

    {

      return (sz - sizeof(size_type) -

          pool::ct_lcm<sizeof(size_type), sizeof(void *)>::value);

    }

返回除去后面两个薄记的小块后的大小

 

    size_type & next_size() const

    { return *(reinterpret_cast<size_type *>(ptr_next_size())); }

返回下一个大块的大小,这里先把第二个小块转化为size_type(通常情况下是size_t类型)的指针,然后再取值.这里返回的也是一个引用

 

    char * & next_ptr() const

    { return *(reinterpret_cast<char **>(ptr_next_ptr())); }

先说说这种指针的用法

     char a[] = {10,0,0,0};

     char * p = a;

     char ** pp = reinterpret_cast<char**>(p);

     char * pother = *pp;

cout<<reinterpret_cast<int>(pother)<<endl;

这里将会输出10,也就是把p指向的内存块开始的连续四个字节存放的值,赋值给pother.这里reinterpret_cast<char**>(p);先将p转化为char**,那么p所指向的内存块中存放的值就是一个char*类型的指针.

在这个结构里ptr_next_size();返回指向最后两个小块的第一个内存块的指针,这个内存块存放就是指向下一个大块的指针的值, return *(reinterpret_cast<char **>(ptr_next_ptr()));将那个值转化为一char*的指针返回,那个返回的指针指向的是下一个大块的第一个字节

PODptr next() const

    { return PODptr<size_type>(next_ptr(), next_size()); }

返回一个包装下一个大块的PODPtr结构.

 

    void next(const PODptr & arg) const

    {

      next_ptr() = arg.begin();

      next_size() = arg.total_size();

    }

用来填充最后面的两个小块,这样可以方便把它串成一个链表

 

PODPtr分析完了,它起的只是一个辅助作用,它针对一块特定形式的内存,提供一些成员函数,方便把这些大块内存串接起来

 

下面再看一个辅助结构,这个结构很重要,在看之前,也先看看这个结构怎么管理分配的内存

http://hi.baidu.com/lyskyly/album/item/bfb1a4fb02df6d234e4aea71.html

图二

上面的图表示这个大内存块被多次分配和释放后的情况,其中绿色小块,表示已经分配出去了的内存,蓝色表示还没有被分配出去的空闲内存块.在绿色小块中存了东西,它存放了指向下一个空闲小块的指针.这样,所有空闲小块就被串了起来.

template <typename SizeType>

class simple_segregated_storage

{

 public:

    typedef SizeType size_type;

  private:

simple_segregated_storage(const simple_segregated_storage &);

  void operator=(const simple_segregated_storage &);

    static void * try_malloc_n(void * & start, size_type n,size_type partition_size);

  protected:

    void * first;

    void * find_prev(void * ptr);

 public:

    simple_segregated_storage()

    :first(0) { }

    static void * segregate(void * block, size_type nsz, size_type npartition_sz,

                            void * end = 0);

void add_block(void * const block,

    const size_type nsz, const size_type npartition_sz);

    void add_ordered_block(void * const block,const size_type nsz, const size_type npartition_sz);

     bool empty() const { return (first == 0); }

void * malloc();

    void free(void * const chunk);

  void ordered_free(void * const chunk);

    void * malloc_n(size_type n, size_type partition_size);

    void free_n(void * const chunks, const size_type n,const size_type partition_size);

    void ordered_free_n(void * const chunks, const size_type n, const size_type partition_size);

};

这个类中,一开始两句是把拷贝构造函数和赋值操作符给禁用了.

先来看这个函数

    static void * & nextof(void * const ptr)

{ return *(static_cast<void **>(ptr)); }

和上面的一样,这个函数负责,ptr所指内存块存放的值作为一个指针返回,和上面一样,所有的空闲小块都是通过这种方式串接起来的,串上最后一个小块存的值当然是0.这里返回的是指针的引用,这意味这可以通过这个函数     nextof(ptr) = XXXX; 的形式改变ptr所指内存块存放的值.相当于调整这个链表中next指针的值.

segregate(void * block, size_type nsz, size_type npartition_sz,void * end = 0);

这个函数负责把串进来的block这样一个大内存块,化分为npartition_sz大小的空闲小块,再把这些小块用上面的方法串起来,最后返回指向这个大块内存的指针.这个函数有第四个参数,end,它让这个链表的最后一个元素指向end,这样可以把多个大块内存的空闲内存块串起来.

再来看看pool的定义

template <typename UserAllocator>

class pool: protected simple_segregated_storage<

typename UserAllocator::size_type>

这上面pool保护继承自simple_segregated_storge.UserAllocator可以选择是用malloc分配还是用new分配内存,如果原意也可以自己定义分配方式.

Pool中有个store()函数, simple_segregated_storage<size_type> & store() { return *this; }

它将pool转换为simple_segregated_storage类型,返回引用.

下面看一段代码

     boost::pool<> p(sizeof(int));

     int * ptr = static_cast<int*>(p.malloc());

     int * ptr2 = static_cast<int*>(p.malloc());

 p.malloc的第一次调用

    void * malloc()

    {

      // Look for a non-empty storage

      if (!store().empty())

        return store().malloc();

      return malloc_need_resize();

    }

调用这个函数,第一次store().empty()肯定是空的,所以调用malloc_need_resize();来先分配一块内存.

template <typename UserAllocator>

void * pool<UserAllocator>::malloc_need_resize()

{

  // No memory in any of our storages; make a new storage,

  const size_type partition_size = alloc_size();

  const size_type POD_size = next_size * partition_size +

      details::pool::ct_lcm<sizeof(size_type), sizeof(void *)>::value + sizeof(size_type);

  char * const ptr = UserAllocator::malloc(POD_size);

  if (ptr == 0)

    return 0;

  const details::PODptr<size_type> node(ptr, POD_size);

  next_size <<= 1;

 

  //  initialize it,

  store().add_block(node.begin(), node.element_size(), partition_size);

 

  //  insert it into the list,

  node.next(list);

  list = node;

 

  //  and return a chunk from it.

  return store().malloc();

}

这个函数指定分配的内存块的大小, char * const ptr = UserAllocator::malloc(POD_size);这里进行实质性的内存分配.

然后用PODptr处理,这样可以把大块连接起来node.next(list);来做这个连接

store().add_block(node.begin(), node.element_size(), partition_size);

这里还用到simple_segregated_storageadd_block(node.begin(), node.element_size(), partition_size);函数.

    void add_block(void * const block,

        const size_type nsz, const size_type npartition_sz)

    {

      // Segregate this block and merge its free list into the

      //  free list referred to by "first"

      first = segregate(block, nsz, npartition_sz, first);

}

这个函数实际上调用的segregate用来划分这个大块内存

其中还有句next_size <<= 1;,next_size加倍

然后第二次调用int * ptr2 = static_cast<int*>(p.malloc());

      if (!store().empty())

        return store().malloc();

这次直接调用的是simple_segregated_storagemalloc

    void * malloc()

    {

      void * const ret = first;

 

      // Increment the "first" pointer to point to the next chunk

      first = nextof(first);

      return ret;

    }

直接把第一个小块内存返回,然后条件frist指针,让它指向下一个空闲块

释放内存的时候用

p.free(ptr);

这里调用

    void free(void * const chunk)

    { store().free(chunk); }

这个函数再调用simple_segregated_storagemallocfree

    void free(void * const chunk)

    {

      nextof(chunk) = first;

      first = chunk;

 }

这里只是把chunk设为空闲内存的第一块

然后malloc_n,free_n函数是一样的,只是malloc_n一次地址分配连续的n个小内存块

还有一组函数

  void * ordered_malloc();

  void * ordered_malloc(size_type n);

  void ordered_free(void * const chunk);

  void ordered_free(void * const chunks, const size_type n);

这一组函数可以让所有空闲内存的按照地址由小到大排列,包括所有大内存块中的空闲内存,这就要求大块内存也要有序排列.下面这个图是上面那组函数管理的内存块情况,它不会出现图二那种,排在大块最后的那个蓝色块指向前面的空闲块.

这个pool就是这样管理内存块的.里面还提供了一些辅助函数用来对PODptr链表和空闲块链表进行管理.

最后两个函数

release_memory

purge_memory用来收回所有分配的内存,防止内存泄漏

http://hi.baidu.com/lyskyly/album/item/a6427806375f0a7a03088172.html

图三

好像写得太多了,前面部分太罗嗦了.

 

 

你可能感兴趣的:(struct,list,Integer,Class,insert,merge)