kernelpwn slub/slab
总结一波内核分配内存(堆)的策略,不然做内核题总是半懂不懂的。
内核管理内存页面使用了两种算法,伙伴算法和slub算法
伙伴算法
伙伴算法是以页4k为单位管理内存的,也就是申请和释放的最小单位是4k,但是程序多数情况下不会申请这么大的内存,而是比较小的内存,就像用户态的堆一样,所以还需要一个内存管理算法支持这种功能,也就是slub算法
slub
像libc申请释放堆块一样,slub也把堆进行进行了分组管理,每一组的大小是2^3,2^4….2^11字节,堆的大小是以8字节递增的,也就是(8,2048),当超过2048的时候就会使用伙伴系统提供的接口直接申请一个完整的页面,还有两个特殊的组96字节和192字节,这些组映射到kmalloc_caches[12]数组中的一个元素
这是用于slub管理内存的数据结构,可以看见每个kmalloc_caches元素又各种对应一个kmem_cache结构体,这个结构体中又能指向kmem_cache_cpu
和kmem_cache_node
两个数组,这两个结构体才是slub分配算法的关键
有一个博客比喻的很好,kmem_cache_node
相当于仓库,kmem_cache_cpu
是真正用于分配内存的地方,每个kmalloc_caches元素都有自己的kmem_cache_cpu
和kmem_cache_node
。
kmem_cache_cpu和keme_cache_node
slub会根据这个组的大小向伙伴算法申请整数倍4k的内存给kmem_cache_cpu,申请到这块内存叫做slab,其中kmem_cache_cpu.page就指向了slab的首地址,slub会根据这个组的大小把slab切割成很多个小的object,每个object的大小就是这个组的大小,当object还未被申请时候会有八个字节储存下一个object的地址,这样其实这个slab就被切割成大小相等的单向链表了,然后kmem_cache_cpu.freelist指向这个slab的第一个空闲object
当申请这个组的内存的时候如果这个组的kmem_cache_cpu没有一个slab的时候,slub就会申请一个slab并执行上述操作,然后把第一个obj标记为占用并返回给用户,下图体现了这个操作,申请了一个object以后kmem_cache_cpu就会根据这个object的next指向下一个空闲object。
至于为什么说kmem_cache_cpu为什么是内存申请的主要地方现在就很明朗了。
可是kmem_cache_cpu指向的slab总有用完的时候,当这个slab用完怎么办,这时候就得用上kmem_cache_node
这个结构体仓库了,kmem_cache_node有两个指针partial
和full
指向两个链表,其中partial指向有空闲obj的slab链表,full指向没有空闲的slab链表,如下图
当keme_cache_cpu指向的slab用完后就会放到kmem_cache_node.full上面,然后检查keme_cache_partial链表有没有有空闲的slab,如果有的话就会把第一个空闲的obj返回给用户,然后把这个slab给kmem_cache_cpu。
如果kmem_cache_node.full上面的一个slab的一个obj被释放后这个slab就会移向partial。
总结
这是slub算法的主要策略,不涉及源码解读(因为看不懂),先学着看,以后如果有必要会看看。