1,分配器API
http://blog.csdn.net/kickxxx/article/details/9287003
伙伴系统只能分配2的整数幂个页。因此申请时,需要指定请求分配的阶。
2,分配页,
所有分配伙伴系统的函数分配页最终都会调用到alloc_pages_nodemask()函数
static inline struct page*alloc_pages_nodemask(int nid,gfp_t gfp_mask,unsigned int order)
nid:指的是从哪一个节点分配
gfp_mask:一些标志
order:分配阶
该函数主要分为两大步:
1,选择页,在某一个节点的某一个可迁移区域找到足够的连续的页,流程是从指定的管理区开始扫描管理区-->找到充足的管理区从认定的管理区开始遍历,直到找到一个拥有足够空间的管理区例如,如果high_zoneidx对应的ZONE_HIGHMEM,则遍历顺序为HIGHMEM-->NORMAL-->DMA如果high_zoneidx对应ZONE_NORMAL,则遍历顺序为NORMAL-->DMA-->从指定的迁移类型链表中分配内存-->如果在指定迁移类型中找不到则到其他的迁移类型中去寻找(get_page_from_freelist()) ,如果第二步在各个区域都找不到可以满足分配的内存了,那么说明管理区的内存已经确实不够了,于是开始启用一条慢速的途径来分配( __alloc_pages_slowpath),包括尝试去换出一些不经常使用的页等等,内核会在这次分配中表现得更为积极,其中的细节涉及到了其他一些复杂的东西
2,在第一部中只是确定有那么多的空闲的页,还不确定其是否是连续的,如果是连续的则移除页,将要分配的页从当前的伙伴系统移除~可能要按照伙伴系统的方式分解并重排内存分区。如果只是分配一页,内核会进行优化,分配一页的情况不会从伙伴系统分配,而是取自per-CPU缓存,如果当前per-CPU缓存为空,那么需要调用rmqueue_bulk()函数从伙伴系统分配适当的页填充到per-CPU缓存里,如果是要分配多于一页的内存,那么会调用__rmqueue()函数从伙伴系统分配并分解重排内存分区。
如果当前伙伴系统中不能找到足够的空闲页内存~那么内核将采取的动作是:失败的话就尝试慢速分配,一般流程就是唤醒内存页面回收线程,然后尝试低水位分配 -> 忽略水位分配 -> 压缩内存分配 -> 直接回收内存分配 -> oom killer杀死线程分配 -> 压缩内存分配。