c# 结构体 4字节对齐_SLUB内存管理——1.基础数据结构

我们知道linux中有slab分配器、slub分配器、slob分配器三种分配器来管理细粒度的内存,通常小于一个页。其中slab分配器由于实现复杂,因此linux在2.6.22内核中引入了slub分配器,且把它作为linux中的默认分配器使用,而slob分配器一般用于嵌入式系统中,三种分配器的实现复杂程度:slab>slub>slob。一般系统中会建立一些特定大小的slab内存,如16字节、32字节等,此外也会建立一些常用数据结构的slab对象,像file、task_struct结构体等。本文着重讲解下slub的实现。

slub分配器对缓存列表是每个cpu节点单独维护,并使用了一个独立的线程来维护全局的slab对象,一个cpu不适用的对象会被放到全局的partial队列,供其他cpu使用,平衡各个节点的slab对象,回收page时,slab对象时全局失效的,不会引起对象共享问题。

1.基本数据结构

一个slub分配器对应一个内存缓存kmem_cache,kmem_cache中管理着各个内存块slab,这里的slab有别于slab分配器,一个slab通常由一个或多个page组成,一个slab中又细分成各个对象object,这个object通常就是我们申请到的内存,所以kmem_cache和slab数据结构体可以认为是对object的管理,其中对slab的描述在struct page中,老版本中原来还有一个struct slab的结构体。同时为了缓存和支持percpu,又引入了kmem_cache_cpu和kmem_cache_node结构体。对于kernel4.9而言,图中kmem_cache_cpu中的node应该是partial

b0c8074f15c99b92fe6fe4326ca06abf.png
图片来源:https://blog.csdn.net/ctthuangcheng/article/details/8915230

通常linux系统中会根据object的大小,创建各个slub分配器,一个slub分配器就对应一个kmem_cache,各个kmem_cache以链表的形式挂在slab_caches全局变量下。

include

根据next指针存放的位置,object的内存布局可以分为两种方式,kmem_cache中的offset ,inuse,align等字段的值也有有所不同。

  • next指针放在object对象后面

6e4927f5c0dceae69049edbe33f99108.png
  • next指针放在object对象内部

void 指针能放在object中是因为当object没有被分配出去的时候,其内存时空闲的,因此可以用来放置next指针,当object被分配出去的时候,其next指针也需要了,一般使用了debug模式时,使用第一种方式,否则使用第二种方式。 这里采用word align字节对齐,主要为弥补cache L1对齐而填充字节

271c77dd6977da74a3d5a7c7e1e9e3ba.png
  • cpu_partial

该值表示每个cpu上partial 链表上的slab空闲的object数量,如果cpu上的空闲object小于这个值,那么需要到对应的node上获取几个部分空闲的slab

  • min_partial

该值表示每个node节点上partial链表上slab的数量不能小于这个值,否则的话需要将空闲的slab加入到node节点上的partial链表中

slub分配器中kmem_cache_cpu缓存是以slab为单位,而slab分配器的kmem_cache_cput缓存是以object为单位,这是两者的差别之一。这里快速的原因就是当我需要一个object或者一个slab的时候不需要再从kmem_cache_ndoe的partial链表上查找slab,而是直接由page指定的slab就可以了。

include

每个内存节点对应一个kmem_cache_node实例,这么设计的原因主要是为了支持numa系统。对于slub而言kmem_cache_node比较简单,这里partial链表相当于slab中的空闲对象链表和部分空闲对象链表加在一起,nr_partial表示链表节点的数量。如果page的objects==inuse,那么表示page是full,或者page的inuse==0,表示page是free的,这两种情况page都会从partial链表上移走。debug模式下还有full链表,此时full的slab就会放到full链表中。

mm

当用page描述slab的时候,这里的一个page表示一个slab,不再是表示一个物理页,而是表示2^order个连续的联合页,这个order是由kmem_cache里的oo或者min来决定的,通过一开始需要申请的object的size来计算合理的order。

struct 

看完上面的数据结构,总感觉有些成员在不同的数据结构中都有存在,比如:

  • kmem_cache_cpu上的freelist和page的freelist
  • kmem_cache_cpu上的partial和kmem_cache_node上的partial

这里简单介绍下两者之间的不同,首先需要明白一点kmem_cache_node上的freelist 和partial链表都是全局的,那么在不同cpu下使用一个page或者object免不了要加锁,所以才有了kmem_cache_cpu,通常kmem_cache_cpu从kmem_cache_node上获得一个page的时候,page->freelist会置NULL,这里置NULL的原因是确保其他cpu不会再从这个page上获取object,所以这里就相当于给page的freelist加锁;而cpu_slab->page指向这个page,就相当于给kmem_cache_node的partial加锁,当该cpu上的node id和该page对应的node不匹配的时候,会释放这个page,归还到kmem_cache_node里,page->freelist重新指向page->freelist所指向的object,供其他cpu分配使用,这就是kmem_cache_cpu和page都有freelist的原因。只要明白同一个page的object可以被cache到不同的cpu,同时又没有锁的保护,大概就能明白上面的意图

另外还需要注意一点,struct page中counters和struct{unsigned inuse,unsigned object,unsigned frozen}结构体同在一个union中,set_page_slub_counters函数有这样一句注释:

“page->counters can cover frozen/inuse/objects as well as page->_refcount”

代码中很多类似的操作,新建一个struct page new,将page的counters值赋给new.counters,因为这里是union,且inues、object、frozen总共加起来是32位,那么就相当于将page中的这个结构体赋值给了new,因此你会看到有new.inuse--的操作,但实际却没有看到对inuse的初始化操作,明白这一步很关键。这样的好处还有一个是如果想知道inuse、object、frezen是否有变化,只要查看counters的值就好了。

。。。。。。
    

2.slub初始化

slub的初始化系统mm初始化完成以后进行

d749d495b922b116e03886ca9df77b75.png

d357b550c9f745b7e0caf4978122ef6c.png
void 

kmem_cache_init中主要创建初始的“kmem_cache” 和“kmem_cache_node”,后续创建的kmem_cache和kmem_cache_node都是从这两个初始缓存中创建而来,并创建通用缓存。一个kmem_cache维护一组kmem_cache_cpu,分别对应每一个cpu,一般情况下kmem_cache_cpu只提供一个page的缓存。

3.参考

https://www.cnblogs.com/mysky007/p/12316168.html

https://blog.csdn.net/woweimkuang/article/details/17553169

https://www.jeanleo.com

https://www.cnblogs.com/chaozhu/p/10157241.html

https://blog.csdn.net/ctthuangcheng/article/details/8915230

http://blog.chinaunix.net/uid-26859697-id-5561360.html

《How does the SLUB allocator work》——Joonsoo Kim

《linux内核深度分析》

《深入理解linux内核》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值