slab 内存管理在现代C++中的应用

来源:互联网 发布:端口号怎么查看 编辑:程序博客网 时间:2024/05/01 15:52
   大四一段时间曾经学习过 Linux 内核的一小部分,自然读过大名鼎鼎的 slab 内存管理;后来又看了看<Modern C++ Design>中的 Small Object Allocator, 觉得它们在原理上大致相同。这里仅将其基本原理简单介绍一下,以飨读者。
    1. 先说 slab
    Linux 的 slab 内存管理由两个部分组成,一是所谓的 cache, 再就是 slab;每一种数据结构对应一个 cache,举例来说,struct task_struct (这个结构非常大,就是所谓的进程控制块)对应 taskstruct_cachep, 而这种数据结构在分配内存时就只能从对应的 cache 中分配,也即,要分配一个 struct task_struct, 就只能
kmem_cache_alloc(taskstruct_cachep);总体上,这个 cache 管理着一个对象(object)池,分配内存时,直接从池中取空闲的对象,而释放时则返还给这个对象池,只有在池为空时才向系统申请内存页;所以这个对象池是非常快的。
    这个内存池由一个cache管理,由多个 slab 组成,每个 slab 可以缓存多个对象。注意,slab是固定大小的而且都是一个页的大小(4K), 这是为了计算的方便,后面就会看到。cache 和 slab 的结构如图所示:
                         _________          _________
   cachep----->|  slab         |------>|  slab        |---->......
                     |                 |          |               |
                     |                 |          |               |
                     | object       |           |  object     |
                     | object       |           |  object     |
            (4K)   |___  _____|    (4K)|________|
    一个 slab 又由两部分组成,这个4K的内存块的前面的一小部分是 slab 控制块(Linux 里面稍有不同),紧接着就是一组 object。slab控制块维护着一个指针,指向当前空闲着的对象。当一个对象被释放时,就让这个指针指向这个刚释放的对象;而在申请内存时直接将这个指针指向的空闲对象返回即可。在释放时需要找到对象对应的slab,那么如何找到它呢?直接将其指针圆整到4K就是相应 slab 控制块的首地址!
    你会猜,既然每一种数据结构都需要一个 cache,那么内核中肯定有不少 cache 吧?猜对了! Linux 内核中确实有不少 cache, 而且每一种重要的数据结构都有一个 cache;然而,仔细观察发现,cache其实是以数据结构的大小为分界线的,两个数据结构,只要大小相同,就可以共用一个 cache,于是又有了 kmalloc,它管理着几个 cache,内核中没有 cache 的数据结构都从 kmalloc 中分配;为了使内存管理器更加通用,我们将要实现的内存管理器也将以大小为中心。
    实现通用性是有代价的,以数据结构为中心的 cache 在释放内存时只需传入数据结构的指针,而以大小为中心的 cache 还要传入内存块的大小;不过,在C++的内存分配中,我们可以很容易地解决这个问题。

    2. 改造的 slab
     我们不能为每一种数据结构都实现一个 cache,但我们可以为每一种大小的数据结构实现一个 cache。这里要解决的问题是从数据结构的大小找到对应的 cache。我们的解决办法如下:从1到512字节大小的对象的 cache,用数组,大于512字节的,用 std::map或hash_map;最终,内存分配的数据结构及接口如下:

       struct kmem_slab
       {
          struct kmem_cache*   cache;
          struct kmem_slab*      next;   // next slab of the same cache
          char    *freeblock;    // linked list of free blocks, NULL if none
          char    *chunk;        // linked list of all blocks
          size_t    chunkSize;    // sizeof the chunk
          size_t    blocks;        // total number of blocks (chunkSize / blockSize)
          size_t    refCount;    // How many blocks are allocated.
       };

        struct kmem_cache
        {
            struct kmem_slab* slabs;    // slabs are linked into a list
            struct kmem_slab* freeslab;    // slab that may have free block
            size_t blockSize;            // block size of a block
            // map from block to slab, only used in large object
            KSlabMap *slabmap;
        };

// for small objects, we store the caches in static array;
// for large objects, we store the caches in a map.
// Memory is aligned, so we don't need KMM_MAX_SMALLOBJECT_SIZE kmem_caches
//
static struct kmem_cache smallCache[KMM_MAX_SMALLOBJECT_SIZE / KMM_ALIGN];

/*
 * Mapping from block size to corresponding kmem_cache(large objects).
 */
static KCacheMap *largeCache;

      void* kmemcache_alloc(size_t nBytes);
      void kmemcache_free(void* ptr, size_t nBytes);

    3.  在C++中使用 slab
      有两种方法,一,虚基类,二,宏
     (1) 虚基类
      class KObject {
      public:
             virtual ~KObject(){}
             static void* operator new(size_t nBytes) {
                   return kmemcache_alloc(nBytes);
             }
             static void operator delete(void* ptr, size_t nBytes) {
                   kmemcache_free(ptr, nBytes);
             }
       }
       凡是从 KObject 继承的类都具备这种基于 cache 的分配能力。注意,virtual ~KObject(){}是必须的,否则编译器无法知道对象的实际大小,<Modern C++ Design>里有对这一点的详细说明。
    (2) 宏
      #define OBJECT_ALLOCATOR() /
         public:/
          static void* operator new(size_t nBytes) {/
                   return kmemcache_alloc(nBytes);/
             }/
             static void operator delete(void* ptr, size_t nBytes) {/
                   kmemcache_free(ptr, nBytes);/
             }
       把它放在每一个类中就可以了。

参考:
1. Modern C++ Design
2. The Slab Allocator: An Object-Caching Kernel Memory Allocator
3. Understanding the Linux Kernel
4. Linux 内核源代码情景分析
原创粉丝点击