genalloc — 通用内存分配器

来源:互联网 发布:c语言 函数库啥意思- 编辑:程序博客网 时间:2024/06/05 15:38

genalloc 是 linux 内核提供的通用内存分配器,源码位于 lib/genalloc.c。这个分配器为独立于内核以外的内存块提供分配方法,采用的是最先适配原则,android 最新的ION 内存管理器对 ION_HEAP_TYPE_CARVEOUT 类型的内存就是采用的这个分配器。

1、基础数据结构

首先看下分配器用到的几个数据结构,struct gen_pool 用来描述一个内存池:

[cpp] view plaincopy
  1. struct gen_pool {  
  2.     rwlock_t lock;             /* 链表读写锁 */  
  3.     struct list_head chunks;   /* 内存池中内存块的链表 */  
  4.     int min_alloc_order;       /* 内存池最小分配单元的阶数,大小为 2^min_alloc_order */  
  5. };  
在使用的时候需要向内存池中加入内存块,一个内存块即一大块连续的物理内存,用 struct gen_pool_chunk 来描述:

[cpp] view plaincopy
  1. struct gen_pool_chunk {  
  2.     spinlock_t lock;              /* 操作内存块时用到的自旋锁 */  
  3.     struct list_head next_chunk;  /* 加入内存池的节点 */  
  4.     unsigned long start_addr;     /* 内存块的起始地址 */  
  5.     unsigned long end_addr;       /* 内存块的结束地址 */  
  6.     unsigned long bits[0];        /* 内存块的位图 */  
  7. };  

2、函数接口及调用方法

genalloc 用到的函数接口有下面几个:

[cpp] view plaincopy
  1. /* 创建一个内存池,主要工作是完成 struct gen_pool 的初始化 */  
  2. struct gen_pool *gen_pool_create(int min_alloc_order, int nid);  
  3. /* 向内存池中加入内存块,addr 为起始地址,size 为大小 */  
  4. int gen_pool_add(struct gen_pool *pool, unsigned long addr, size_t size, int nid);  
  5. /* 销毁一个内存池 */  
  6. void gen_pool_destroy(struct gen_pool *pool);  
  7. /* 内存池分配内存的函数 */  
  8. unsigned long gen_pool_alloc(struct gen_pool *pool, size_t size);  
  9. /* 内存池释放内存的函数 */  
  10. void gen_pool_free(struct gen_pool *pool, unsigned long addr, size_t size);  
对通用内存分配器的一般使用方法如下:
[cpp] view plaincopy
  1. /* 初始化内存池,需要创建以及加入内存块,参数为:起始地址、大小、最小分配阶数 */  
  2. static void *mm_init(uint32_t addr, uint32_t size, uint32_t order)  
  3. {  
  4.     struct gen_pool *pool;  
  5.   
  6.     pool = gen_pool_create(order, 0);  
  7.     if (pool == NULL) {  
  8.         return NULL;  
  9.     }  
  10.   
  11.     if (gen_pool_add(pool, addr, size, 0) != 0) {  
  12.         gen_pool_destroy(pool);  
  13.   
  14.         return NULL;  
  15.     }  
  16.   
  17.     return pool;  
  18. }  
  19.   
  20. /* 销毁内存池 */  
  21. static void mm_exit(void *handle)  
  22. {  
  23.     gen_pool_destroy(handle);  
  24. }  
  25.   
  26. /* 分配函数 */  
  27. static uint32_t mm_alloc(void *handle, uint32_t size)  
  28. {  
  29.     return gen_pool_alloc(handle, size);  
  30. }  
  31.   
  32. /* 释放函数 */  
  33. static void mm_free(void *handle, uint32_t addr, uint32_t size)  
  34. {  
  35.     return gen_pool_free(handle, addr, size);  
  36. }  
  37.   
  38. /* 提供给上一级内存管理器调用 */  
  39. struct xxx_mem_ops mm_ops = {  
  40.     .init = mm_init,  
  41.     .exit = mm_exit,  
  42.     .alloc = mm_alloc,  
  43.     .free = mm_free,  
  44. };  
3、分配函数解析

genalloc 通过 gen_pool_alloc 函数来分配内存,下面我们分析一下这个函数的代码:

[cpp] view plaincopy
  1. unsigned long gen_pool_alloc(struct gen_pool *pool, size_t size)  
  2. {  
  3.     struct list_head *_chunk;  
  4.     struct gen_pool_chunk *chunk;  
  5.     unsigned long addr, flags;  
  6.     int order = pool->min_alloc_order;  
  7.     int nbits, bit, start_bit, end_bit;  
  8.   
  9.     if (size == 0)  
  10.         return 0;  
  11.   
  12.     nbits = (size + (1UL << order) - 1) >> order;  /* 计算申请的内存需要几个连续的最小单元 */  
  13.   
  14.     read_lock(&pool->lock);  
  15.     list_for_each(_chunk, &pool->chunks) {         /* 遍历内存池 */  
  16.         chunk = list_entry(_chunk, struct gen_pool_chunk, next_chunk);  
  17.   
  18.         end_bit = (chunk->end_addr - chunk->start_addr) >> order;        /* 计算当前内存池长度 */  
  19.         end_bit -= nbits + 1;  
  20.   
  21.         spin_lock_irqsave(&chunk->lock, flags);  
  22.         bit = -1;  
  23.         while (bit + 1 < end_bit) {  /* 循环查找最先适配的内存区 */  
  24.             bit = find_next_zero_bit(chunk->bits, end_bit, bit + 1);     /* 寻找为0的bit */  
  25.             if (bit >= end_bit)      /* 循环结束 */  
  26.                 break;  
  27.   
  28.             start_bit = bit;         /* 起始位置 */  
  29.             if (nbits > 1) {         /* 如果申请的内存大于一个最小单元,查找连续的nbits个单元 */  
  30.                 bit = find_next_bit(chunk->bits, bit + nbits,bit + 1);  
  31.                 if (bit - start_bit < nbits)  
  32.                     continue;  
  33.             }  
  34.   
  35.             addr = chunk->start_addr + ((unsigned long)start_bit << order);  /* 计算申请的内存的起始地址 */  
  36.             while (nbits--)  
  37.                 __set_bit(start_bit++, chunk->bits);  /* 将申请到的单元全部标记为已用 */  
  38.             spin_unlock_irqrestore(&chunk->lock, flags);  
  39.             read_unlock(&pool->lock);  
  40.             return addr;  
  41.         }  
  42.         spin_unlock_irqrestore(&chunk->lock, flags);  
  43.     }  
  44.     read_unlock(&pool->lock);  
  45.     return 0;  
  46. }  

因为是用的最先适配原则,所以逻辑比较简单,我们也可以根据自己的需求实现最适合分配器以及伙伴分配器。

附1、buddy 分配器

平台实现了一个 buddy 分配器,代码很精致:

[cpp] view plaincopy
  1. struct buddy_unit {  
  2.     uint8_t         used:1;     /* 1 if allocated, 0 if free */  
  3.     uint8_t         order:7;    /* size of the region in buddy space */  
  4. };  
  5.   
  6. struct buddy_pool {  
  7.     uint32_t        addr;       /* the start addr of the buddy area */  
  8.     uint32_t        size;       /* the total size of the buddy area */  
  9.     uint32_t        order;      /* the base order of each unit */  
  10.   
  11.     uint32_t        total;      /* total units */  
  12.     uint32_t        free;       /* unused units */  
  13.   
  14.     struct buddy_unit   *bitmap;    /* the bitmap of all buddy units */  
  15.     spinlock_t      lock;  
  16. };  
  17.   
  18. struct buddy_pool *buddy_create(uint32_t addr, uint32_t size, uint32_t order)  
  19. {  
  20.     struct buddy_pool *pool;  /* buddy内存池 */  
  21.     uint32_t end;  
  22.     int index, uorder;  
  23.   
  24.     /* 以 2^order = 2^15 = 32KB 为管理单元修正参数 */  
  25.     end = (addr + size) & ~((1 << order) - 1);  /* buddy区结束地址 */  
  26.     addr = addr & ~((1 << order) - 1);          /* buddy区起始地址 */  
  27.   
  28.     if (end <= addr) {  
  29.         return NULL;  
  30.     }  
  31.   
  32.     /* 实例化buddy内存池 */  
  33.     pool = kmalloc(sizeof(struct buddy_pool), GFP_KERNEL);  
  34.     if (pool == NULL) {  
  35.         return NULL;  
  36.     }  
  37.   
  38.     pool->total = size >> order;  /* buddy区以32KB为单元的内存块总数 */  
  39.   
  40.     /* 申请管理位图,每个单元占用一个字节 */  
  41.     pool->bitmap = kzalloc(sizeof(struct buddy_unit) * pool->total, GFP_KERNEL);  
  42.     if (pool->bitmap == NULL) {  
  43.         kfree(pool);  
  44.         return NULL;  
  45.     }  
  46.   
  47.     /* 初始化各参数 */  
  48.     pool->free = pool->total;  
  49.     pool->addr = addr;  
  50.     pool->size = size;  
  51.     pool->order = order;  
  52.     spin_lock_init(&pool->lock);  
  53.   
  54.     mdbg("create buddy: total=%d, order=%d\n", pool->total, pool->order);  
  55.   
  56.     /* initialize the bitmap orders */  
  57.     index = 0;  
  58.     uorder = BUDDY_MAX_ORDER;  /* buddy区最大的分配阶数 */  
  59.     while (index < pool->total) {  
  60.         while (index + (1 << uorder) > pool->total) {  
  61.             uorder--;  /* e.g: 2^24 = 16MB, total = 2^9, uorder = 9 */  
  62.         }  
  63.         pool->bitmap[index].order = uorder; /* 只初始化buddy的第一个成员 */  
  64.         index += 1 << uorder;  
  65.     }  
  66.   
  67.     return pool;  
  68. }  
  69.   
  70. uint32_t buddy_alloc(struct buddy_pool *pool, uint32_t size)  
  71. {  
  72.     int i, order, buddy, index = -1;  
  73.   
  74.     /* 获取申请内存长度的order */  
  75.     for (i = 0; i <= BUDDY_MAX_ORDER; i++) {  
  76.         if ((1 << (pool->order + i)) >= size) {  
  77.             break;  
  78.         }  
  79.     }  
  80.     if (i > BUDDY_MAX_ORDER) {  
  81.         minfo("size is too big: 0x%08x\n", size);  
  82.         return 0;  
  83.     }  
  84.     order = i;  
  85.   
  86.     spin_lock(&pool->lock);  
  87.     i = 0;  
  88.     do {  /* 扫描管理位图 */  
  89.         mdbg("index = %d\n", index);  
  90.         /* 判断当前内存单元是否已经被申请 */  
  91.         if (pool->bitmap[i].used == 0) {  /* 如果没有被申请 */  
  92.             if (order == pool->bitmap[i].order) {  /* 如果当前order与申请的order相同则跳出循环 */  
  93.                 /* matched order */  
  94.                 index = i;  /* 将编号赋给index */  
  95.                 break;  
  96.             } else if (order < pool->bitmap[i].order) {  /* 如果申请order小于当前order */  
  97.                 /* get the best fit buddy unit */  
  98.                 if (index == -1) {  /* 如果是第一次匹配 */  
  99.                     index = i;  
  100.                 } else if (pool->bitmap[i].order < pool->bitmap[index].order) {/* 当前order小于上次匹配的order */  
  101.                     index = i;  /* 则将匹配order更新为当前值,这样可以保证是用的最小的buddy来分裂 */  
  102.                 }  
  103.             }  
  104.         }  
  105.         i += 1 << pool->bitmap[i].order;  /* 移动到下一个可以分配单元 */  
  106.     } while (i < pool->total);  
  107.   
  108.     /* no free buddy unit found */  
  109.     if (index == -1) {  
  110.         spin_unlock(&pool->lock);  
  111.         minfo("no buddy found!\n");  
  112.   
  113.         return 0;  
  114.     }  
  115.   
  116.     /* 如果申请order小于找到的order,则将找到的buddy分裂,直到相等 */  
  117.     while (order < pool->bitmap[index].order) {  
  118.         pool->bitmap[index].order--;  /* 将当前buddy的order减一 */  
  119.         /* Buddy# = Slot# ^ (1 << order) */  
  120.         buddy = index ^ (1 << pool->bitmap[index].order);       /* 找到分裂出来的buddy */  
  121.         pool->bitmap[buddy].order = pool->bitmap[index].order;  /* 将分裂出来的buddy的order也减一 */  
  122.     }  
  123.   
  124.     pool->bitmap[index].used = 1;      /* 将找到的buddy标记为已用 */  
  125.     pool->bitmap[index].order = order; /* 重复赋值? */  
  126.     pool->free -= (1 << order);        /* 总的空闲单元数减去申请成功的单元数 */  
  127.   
  128.     spin_unlock(&pool->lock);  
  129.   
  130.     return pool->addr + (index << pool->order);  /* 返回申请到内存块的起始物理地址 */  
  131. }  
0 0