词条 | 内存池 |
释义 | 基本概念内存池(Memory Pool)是一种内存分配方式。 通常我们习惯直接使用new、malloc等API申请分配内存,这样做的缺点在于:由于所申请内存块的大小不定,当频繁使用时会造成大量的内存碎片并进而降低性能。 内存池则是在真正使用内存之前,先申请分配一定数量的、大小相等(一般情况下)的内存块留作备用。当有新的内存需求时,就从内存池中分出一部分内存块,若内存块不够再继续申请新的内存。这样做的一个显著优点是尽量避免了内存碎片,使得内存分配效率得到提升。 在内核中有不少地方内存分配不允许失败. 作为一个在这些情况下确保分配的方式, 内核开发者创建了一个已知为内存池(或者是 "mempool" )的抽象. 一个内存池真实地只是一类后备缓存, 它尽力一直保持一个空闲内存列表给紧急时使用. 一个内存池有一个类型 mempool_t ( 在 <linux/mempool.h> 中定义); 你可以使用 mempool_create 创建一个: mempool_t *mempool_create(int min_nr, mempool_alloc_t *alloc_fn, mempool_free_t *free_fn, void *pool_data); min_nr 参数是内存池应当一直保留的最小数量的分配的对象. 实际的分配和释放对象由 alloc_fn 和 free_fn 处理, 它们有这些原型: typedef void *(mempool_alloc_t)(int gfp_mask, void *pool_data); typedef void (mempool_free_t)(void *element, void *pool_data); 给 mempool_create 最后的参数 ( pool_data ) 被传递给 alloc_fn 和 free_fn. 如果需要, 你可编写特殊用途的函数来处理 mempool 的内存分配. 常常, 但是, 你只需要使内核 slab 分配器为你处理这个任务. 有 2 个函数 ( mempool_alloc_slab 和 mempool_free_slab) 来进行在内存池分配原型和 kmem_cache_alloc 和 kmem_cache_free 之间的感应淬火. 因此, 设置内存池的代码常常看来如此: cache = kmem_cache_create(. . .); pool = mempool_create(MY_POOL_MINIMUM,mempool_alloc_slab, mempool_free_slab, cache); 一旦已创建了内存池, 可以分配和释放对象,使用: void *mempool_alloc(mempool_t *pool, int gfp_mask); void mempool_free(void *element, mempool_t *pool); 当内存池创建了, 分配函数将被调用足够的次数来创建一个预先分配的对象池. 因此, 对 mempool_alloc 的调用试图从分配函数请求额外的对象; 如果那个分配失败, 一个预先分配的对象(如果有剩下的)被返回. 当一个对象被用 mempool_free 释放, 它保留在池中, 如果对齐预分配的对象数目小于最小量; 否则, 它将被返回给系统. 一个 mempool 可被重新定大小, 使用: int mempool_resize(mempool_t *pool, int new_min_nr, int gfp_mask); 这个调用, 如果成功, 调整内存池的大小至少有 new_min_nr 个对象. 如果你不再需要一个内存池, 返回给系统使用: void mempool_destroy(mempool_t *pool); 你编写返回所有的分配的对象, 在销毁 mempool 之前, 否则会产生一个内核 oops. 如果你考虑在你的驱动中使用一个 mempool, 请记住一件事: mempools 分配一块内存在一个链表中, 对任何真实的使用是空闲和无用的. 容易使用 mempools 消耗大量的内存. 在几乎每个情况下, 首选的可选项是不使用 mempool 并且代替以简单处理分配失败的可能性. 如果你的驱动有任何方法以不危害到系统完整性的方式来响应一个分配失败, 就这样做. 驱动代码中的 mempools 的使用应当少. 内存池实现示例内存池的实现有很多,性能和适用性也不相同,以下是一种较简单的C++实现 -- GenericMP模板类。(本例取材于《Online game server programming》一书) 在这个例子中,使用了模板以适应不同对象的内存需求,内存池中的内存块则是以基于链表的结构进行组织。 GenericMP模板类定义template <class T, int BLOCK_NUM= 50> class GenericMP { public: static VOID *operator new(size_t allocLen) { assert(sizeof(T) == allocLen); if(!m_NewPointer) MyAlloc(); UCHAR *rp = m_NewPointer; m_NewPointer = *reinterpret_cast<UCHAR**>(rp); //由于头4个字节被“强行”解释为指向下一内存块的指针,这里m_NewPointer就指向了下一个内存块,以备下次分配使用。 return rp; } static VOID operator delete(VOID *dp) { *reinterpret_cast<UCHAR**>(dp) = m_NewPointer; m_NewPointer = static_cast<UCHAR*>(dp); } private: static VOID MyAlloc() { m_NewPointer = new UCHAR[sizeof(T) * BLOCK_NUM]; UCHAR **cur = reinterpret_cast<UCHAR**>(m_NewPointer); //强制转型为双指针,这将改变每个内存块头4个字节的含义。 UCHAR *next = m_NewPointer; for(INT i = 0; i < BLOCK_NUM-1; i++) { next += sizeof(T); *cur = next; cur = reinterpret_cast<UCHAR**>(next); //这样,所分配的每个内存块的头4个字节就被“强行“解释为指向下一个内存块的指针, 即形成了内存块的链表结构。 } *cur = 0; } static UCHAR *m_NewPointer; protected: ~GenericMP() { } }; template<class T, int BLOCK_NUM > UCHAR *GenericMP<T, BLOCK_NUM >::m_NewPointer; GenericMP模板类应用class ExpMP : public GenericMP<ExpMP> { BYTE a[1024]; }; int _tmain(int argc, _TCHAR* argv[]) { ExpMP *aMP = new ExpMP(); delete aMP; } |
随便看 |
百科全书收录4421916条中文百科知识,基本涵盖了大多数领域的百科知识,是一部内容开放、自由的电子版百科全书。