Chinaunix首页 | 论坛 | 博客
  • 博客访问: 348668
  • 博文数量: 18
  • 博客积分: 2049
  • 博客等级: 大尉
  • 技术积分: 664
  • 用 户 组: 普通用户
  • 注册时间: 2007-07-06 17:38
文章分类

全部博文(18)

文章存档

2012年(1)

2011年(8)

2010年(4)

2009年(2)

2008年(3)

分类: LINUX

2008-12-12 17:46:38

    SLAB的设计理念是基于对象缓冲的,基本想法是避免重复大量的初始化和清理操作。SLAB也可考虑到了CPU缓冲的有效利用。SLAB主要可以用于频繁分配释放的内存对象。    

    SLAB的设计目的是作为系统的核心缓冲系统,当然,在各个子系统里也可以使用独立的缓冲系统,但这种方法有三个缺点:(选译自:The Slab Allocator: An Object-Caching Kernel Memory Allocator)

  1. 在系统出现内存紧缺时,不便于回收内存。因为无法得知“私有”缓冲的管理情况;
  2. “私有”缓冲可能缺少一个分配器应该提供的统计和调试功能,这不利于操作系统的监视和调试;
  3. 具有类似的多个分配器实现导致内核大小增长。

   在概念上,SLAB的工作方式如下(选译自:The Slab Allocator: An Object-Caching Kernel Memory Allocator):

    分配一个对象:

if (there is an object in the cache)
    take it ( without call constructor());
else {
    allocate memory;
    call constructor() for this new object.
}


    释放一个对象:

return it to the cache; (with destructor required)


    回收内存:

take some objects from the cache;
destroy above objects;
free the underlying memory;


    在实现上,SLAB分配器一般都分成三个抽象层次:
  1. objects,即每个cache管理的最小单位。也是每次分配的单元。
  2. slab,每个slab收集一定数量的object,具体多少依赖于object的大小、页面大小等因素。slab是SLAB分配器的内部结构,对于使用者是不可见的。
  3. cache,包括三种slab:空的、满的、半空半满的。使用SLAB分配器之前,一般都需要先创建一个cache。

    创建一个cache ,一般使用kmem_cache_create(),正如以上伪代码所表明的:这并不意味着SLAB分配器会预先分配任何object。它只是分配一个cache描述符,初始化其成员而已。OK,那么“分配一个cache描述符”是怎么分配的呢?答案是仍然是使用SLAB分配器的标准接口:kmem_cache_alloc(),那么这又要求一个己经创建好的cache。仿佛碰到了“鸡生蛋还是蛋生鸡”问题。

    答案是用于分配cache描述符的cache是系统静态定义和初始化的,这就是cache_cache。

    还有一个通常称为cache_sizes的一系列cache,实际上它们就是一系列预先创建的具有不同object大小的cache,用于常用的kmalloc()/kfree()接口,别无任何特殊之处。

            slab管理性信息包括一些统计性信息和空闲object链表(依靠索引维系链接关系)。根据object的大小和页面大小,它可能保存于objects所在的页面上。如果slab管理信息单独保存,就称这种slab是off slab。如果处于object页面内,它们会出现在页面首部。

    如果每个slab在承载了objects(和可能的管理性信息)之后尚有剩余空间,那么,还可以进行缓冲着色处理,具体可以支持多少种“颜色”,取决于object的对齐要求,L1缓冲参数和剩余空间的大小。注意“剩余空间”的大小不可能超过object的大小。

    在Slab正在扩展空间或者已经扩展了空间之后,这个Slab会设置一个GROWN标志,这样在系统在第一趟回收内存时会先跳过它(在这趟扫描时,它会除掉这个标志,所以之后的扫描不会再对之留情)。

   
    此外,Linux实现上一个有意思的地方是:因为每个页面如果分配给SLAB分配器管理,那么他一定会与一个slab和一个cache关联。Linux会把这两个描述符的指针,赋给对应页面的页框描述符的list.prev/list.next2,4内核)或者lru.prev/lru.next2.6内核)。这样,在释放一个object时,通过从object地址找到的页框地址就可以方便的定位到对应的slabcache描述符了。这种页面都会设置上PageSlab标志。

    在SMP内核里,每个slab有一个为每个处理器安排的缓冲池,每个CPU里可以有limit个object,每次需要补充这个缓冲池时,会补充batchcount个object。limit取决于object的大小,batchcount取决于limit,在2.4.22内核里batchcount=limit/2。

     SLAB避免重复对象的初始化操作和清理操作。除了节省了这部分代码执行时间之外,这种跳过操作,还避免了初始化/清理代码污染了hotpath代码的缓冲,也即减少了cache footprint。但这种节省也意味着,我们在使用SLAB时需要注意:

  1. 建立SLAB cache时,你指定的constructor和destructor并不是在之后每次对象分配都调用的,所以,他们至少不能具有统计功能;
  2. 在归还对象给SLAB分配器时,你必须保证对象已经恢复到初始状态。如果对象包括诸如引用计数,指针之类的成员时尤其要注意。

    此外,关于缓冲着色,如果你确定需要这个功能,不要以为SLAB分配器的实现会一定满足它的需要,例如对于3000字节的对象,它恐怕是做不到着色效果的。因为每个slab只能保存这样大小的一个object。而每个slab的object数据都会从一个页面的固定偏移开始,所以,你会发现所有3000字节的对象都有一样的页内偏移,这样的话,基本也就没有什么着色而言啦。

        SLAB的设计很棒,但这并不妨碍它成为我们砸自己脚的工具。   


   
    行文数周之后,我才又返回到相关的工作中:原来我们有大量的内部缓冲就是使用slab分配的,学习SLAB实现之后,我非常怀疑这种方法存在着大量的内存浪费,两天的简单修改,充分印证了我的猜测,50行不到的调整居然节省出了190MB的内存,太可怕了!
阅读(2581) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~