在Linux当中,物理内存的划分之前已经写过一篇了,今天来讲讲内存的具体分配
首先,内存被分成一个一个的节点,每个节点由不同的区域组成,当在区域内部需要使用物理内存的时候,就是今天要讲的伙伴系统登场的时候了。
首先,各个内存区域的空闲可用物理内存被分割成一个一个的链表,每个链表当中的元素表示的空闲页的大小都是相同的,且都是2的整数次幂,这一个个的链表,就按照整数次幂(之后叫order)的大小排列在一个数组当中。
当系统需要分配一个大小为k的空间的时候,会先将k按照2^order对其,之后就会先从本地节点上,按照order从小到达的次序去遍历各个链表,直到找到刚好匹配。如果没有刚好匹配,则需要在更大的链表上拿下一个更大块的内存,取出自己需要的之后,还要将剩下的部分塞回到对应order的链表之上。如果当前节点的所有链表均没有匹配,则需要在其他节点上“远程调度”,这种情况对应的消耗会比较大。
以上就是简单的讲述了伙伴系统的功能,其分配的基本单位是页,一般为4k
由于buddy-system的基本单位为4k,但是内核当中的数据结构没有那么大,而且频繁分配释放也会造成大量不必要的消耗,这时候就需要slab分配器出场了(它在嵌入式的兄弟叫slob,大型机上的兄弟较slub),其实slab的功能不仅仅是一个分配器,也是一个缓存管理器,其运行在伙伴系统之上。我们熟知的task_struct等很多内核结构都是由它来管理的。
当我们要申请一个slab缓存的时候,需要制定要缓存的固定类型,比如task_struct,这样,当slab拿到物理内存的时候,它就会把整块的内存排好,只用于存放task_struct,其他的数据类型也一样,另外,所有的slab缓存是通过链表连在一起的。
当确定了slab缓存的类型之后,它就会根据固定类型的数据长度,选取对齐位置,选择和是的padding进行对其,这个padding可以用来设置一些下一个空闲量偏移之类的东西。
这样,当内核需要用到某一种数据类型的时候,就会先根slab去要,slab如果没有,slab就会去找buddy-system,拿到物理内存之后,就按照请求划分,返回调用方想要的。
如果是释放固定的类型,也不是直接返还给物理内存,slab依旧持有,方便下一次调用的是时候,直接从缓存拿,而较少调用buddy-system的次数