21.slab实现(kmem_cache,array_cache,kmem_list3结构)

1. 数据结构
每个缓存由kmem_cache结构的一个实例表示,该结构的内容如下
mm/slab.c 
struct kmem_cache { 
/* 1) per-CPU数据,在每次分配/释放期间都会访问 */ 
struct array_cache *array[NR_CPUS]; 
/* 2) 可调整的缓存参数。由cache_chain_mutex保护 */ 
    unsigned int batchcount; 
    unsigned int limit; 
    unsigned int shared; 
    unsigned int buffer_size; 
    u32 reciprocal_buffer_size; 
/* 3) 后端每次分配和释放内存时都会访问 */ 
    unsigned int flags; /* 常数标志 */ 
    unsigned int num; /* 每个slab中对象的数量 */ 
/* 4) 缓存的增长/缩减 */ 
/* 每个slab中页数,取以2为底数的对数 */ 
    unsigned int gfporder; 
/* 强制的GFP标志,例如GFP_DMA */ 
    gfp_t gfpflags; 
    size_t colour; /* 缓存着色范围 */ 
    unsigned int colour_off; /* 着色偏移 */ 
    struct kmem_cache *slabp_cache; 
    unsigned int slab_size; 
    unsigned int dflags; /* 动态标志 */ 
/* 构造函数 */ 
    void (*ctor)(struct kmem_cache *, void *); 
/* 5) 缓存创建/删除 */ 
    const char *name; 
    struct list_head next; 
/* 6) 统计量 */ 
... 
    struct kmem_list3 *nodelists[MAX_NUMNODES]; 
};
这个冗长的结构分为多个部分,如源代码中的注释所示。开始的几个成员涉及每次分配期间内核对特定于CPU数据的访问,在本节稍后讨论。
 array是一个指向数组的指针,每个数组项都对应于系统中的一个CPU。每个数组项都包含了另一个指针,指向下文讨论的array_cache结构的实例。
 batchcount指定了在per-CPU列表为空的情况下,从缓存的slab中获取对象的数目。它还表示在缓存增长时分配的对象数目。
 limit指定了per-CPU列表中保存的对象的最大数目。如果超出该值,内核会将batchcount个对象返回到slab(如果接下来内核缩减缓存,则释放的内存从slab返回到伙伴系统)。
 buffer_size指定了缓存中管理的对象的长度。
内核对每个系统处理器都提供了一个array_cache实例。该结构定义如下:
mm/slab.c
struct array_cache {
  unsigned int avail;
  unsigned int limit;
  unsigned int batchcount;
  unsigned int touched;
  spinlock_t lock;
  void *entry[];
};
batchcount和limit的语义已经在上文给出。kmem_cache_s的值用作(通常不修改)per-CPU值的默认值,用于缓存的重新填充或清空。
 
avail保存了当前可用对象的数目。在从缓存移除一个对象时,将touched设置为1,而缓存收缩时,则将touched设置为0。这使得内核能够确认在缓存上一次收缩之后是否被访问过,也是缓存重要性的一个标志。最后一个成员是一个伪数组,其中并没有数组项,只是为了便于访问内存中array_cache实例之后缓存中的各个对象而已。
 
kmem_cache和第3、第4部分包含了管理slab所需的全部变量,在填充或清空per-CPU缓存时需要访问这两部分。
 
 nodelists是一个数组,每个数组项对应于系统中一个可能的内存结点。每个数组项都包含struct kmem_list3的一个实例,该结构中有3个slab列表(完全用尽、空闲、部分空闲),在下文讨论。
该成员必须置于结构的末尾。尽管它在形式上总是有MAX_NUMNODES项,但在NUMA计算机上实际可用的结点数目可能会少一些。因而该数组需要的项数也会变少,内核在运行时对该结构分配比理论上更少的内存,就可以缩减该数组的项数。如果nodelists放置在该结构中间,就无法做到这一点。
 objsize是缓存中对象的长度,包括用于对齐目的的所有填充字节。
 num保存了可以放入slab的对象的最大数目。
 free_limit指定了缓存在收缩之后空闲对象数的上限(如果在正常运行期间无需收缩缓存,那么空闲对象的数目可能超出该值)。
 flags是一个标志寄存器,定义缓存的全局性质。当前只有一个标志位。如果管理结构存储在slab外部,则置位CFLGS_OFF_SLAB。相关代码见22.slab分配器(创建缓存kmem_cache_create)
用于管理slab链表的表头保存在一个独立的数据结构中,定义如下:
mm/slab.c
struct kmem_list3 {
  struct list_head slabs_partial; /* 首先是部分空闲链表,以便生成性能更好的汇编代码 */
  struct list_head slabs_full;
  struct list_head slabs_free;
  unsigned long free_objects;
  unsigned int free_limit;
  unsigned int colour_next; /* 各结点缓存着色 */
  spinlock_t list_lock;
  struct array_cache *shared; /* 结点内共享 */
  struct array_cache **alien; /* 在其他结点上 */
  unsigned long next_reap; /* 无需锁定即可更新 */
  int free_touched; /* 无需锁定即可更新 */
};
开始3个表头的语义,在前文已经解释过。free_objects表示slabs_partial和slabs_free的所有slab中空闲对象的总数。
free_touched表示缓存是否是活动的。在从缓存获取一个对象时,内核将该变量的值设置为1。在缓存收缩时,该值重置为0。但内核只有在free_touched预先设置为0时,才会收缩缓存。因为1表示内核的另一部分刚从该缓存获取对象,此时收缩是不合适的。
next_reap定义了内核在两次尝试收缩缓存之间,必须经过的时间间隔。其想法是防止由于频繁的缓存收缩和增长操作而降低系统性能,这种操作可能在某些系统负荷下发生。
free_limit指定了所有slab上容许未使用对象的最大数目。
 
kmem_cache的第3部分包含用于增长(和收缩)缓存的所有变量。
 gfporder指定了slab包含的页数目以2为底的对数,换句话说,slab包含2 gfporder页。
 3个colour成员包含了slab着色相关的所有数据。colour指定了颜色的最大数目,colour_next则是内核建立的下一个slab的颜色。但要注意,该值指定为kmem_list3的一个成员。colour_off是基本偏移量乘以颜色值获得的绝对偏移
量。这也是用于NUMA计算机,UMA系统可以将colour_next保存在struct kmem_cache中。但将下一个颜色放置在特定于结点的结构中,可以对同一结点上添加的slab顺序着色,对本地的CPU高速缓存有好处。
实例——如果有5种可能的颜色(0, 1, 2, 3, 4),而偏移量单位是8字节,内核可以使用下列偏移量值:0×8= 0,1×8 = 8,2×8 = 16,3×8 = 24,4×8 = 32字节。
posted @ 2022-03-22 20:01  while(true);;  阅读(160)  评论(0)    收藏  举报