置疑: 我觉得在linux的slub分配器中,一个内存缓存的BUG

对于SLUB不熟的同学可以先跳过了,涉及的东西比较细致。
简单来说SLUB的结构是N(CPU数)个kmem_cache_cpu,和一个kmem_cache_node组成。其中kmem_cache_cpu的目的是为了从技术层面上提高CPU命中缓存,以及在同一个页面上不出现一个脏的内存(即不同时被多个CPU持有)。我把这个实现机制手工在WINDOWS下实现了一套,在开启多个kmem_cache_cpu的时候出现了个问题:
1.在释放对象的时候,判断是否是当前kmem_cache_cpu页面所在
2.如果是,则直接插入
3.如果不是,释放到邻居节点

如果是单个kmem_cache_cpu肯定没问题,但是在多个kmem_cache_cpu下,很可能会把其中一个kmem_cache_cpu已经持有的页面释放到邻居节点。举个例子:

假设一个页面地址是0-9,
kmem_cache_cpu1,持有A页面,空闲的情况为A0-A5
kmem_cache_cpu2,持有B页面,
当轮到kmem_cache_cpu2执行的时候,一个A页面的地址A6释放,程序检测到非B页面,则直接释放到邻居节点。那么这个时候A页面已经被切割成两段,并且在公共的邻居节点中,其他的kmem_cache_cpu很容易就取到。这个时候反而是增加了内存的脏度

作者: lin_style   发布时间: 2011-01-14

LZ的意思是这个A页面可能被cache到多个kmem_cache_cpu上面去,然后每个kmem_cache_cpu会cache这个page中的部分object,是吧?
仔细看了一下代码,貌似的确是这样子的……没有发现避免一个page被多个CPU cache的代码。

虽然多个CPU都cache了一个page(slub层面),但是它们cache的只是page中的一部分,并且每个CPU cache的部分互不相交。
不过由于CPU缓存跟object不是完全对齐的,CPU与CPU的缓存的确可能存在一定的交叉。从而导致一个CPU上的object写操作引起另一个CPU的缓存失效(这就是LZ所谓的“脏”吧?),尽管两个CPU使用的object不会交叉。

从这一点来看,确实应该避免一个page被两个CPU cache(slub层面)才对……


另外补充一点,我觉得kmem_cache_cpu这个东西除了提高CPU的缓存命中以外,还有一个很大的作用是在object分配与回收时,减少CPU之间的竞争。

作者: kouu   发布时间: 2011-01-14