3.7.4 每处理器页集合
内核针对分配单页做了性能优化,为了减少处理器之间的锁竞争,在内存区域增加1个每处理器页集合。
include/linux/mmzone.h struct zone { … struct per_cpu_pageset __percpu *pageset; /* 在每个处理器上有一个页集合 */ … } ____cacheline_internodealigned_in_smp; struct per_cpu_pageset { struct per_cpu_pages pcp; … }; struct per_cpu_pages { int count; /* 链表里面页的数量 */ int high; /* 如果页的数量达到高水线,需要返还给伙伴分配器 */ int batch; /* 批量添加或删除的页数量 */ struct list_head lists[MIGRATE_PCPTYPES]; /* 每种迁移类型一个页链表 */ };
内存区域在每个处理器上有一个页集合,页集合中每种迁移类型有一个页链表。页集合有高水线和批量值,页集合中的页数量不能超过高水线。申请单页加入页链表,或者从页链表返还给伙伴分配器,都是采用批量操作,一次操作的页数量是批量值。
默认的批量值batch的计算方法如下。
(1)batch = zone->managed_pages / 1024,其中zone->managed_pages是内存区域中由伙伴分配器管理的页数量。
(2)如果batch超过(512 * 1024) / PAGE_SIZE,那么把batch设置为(512 * 1024) / PAGE_SIZE,其中PAGE_SIZE是页长度。
(3)batch = batch / 4。
(4)如果batch小于1,那么把batch设置为1。
(5)batch = rounddown_pow_of_two(batch * 1.5) − 1,其中rounddown_pow_of_two()用来把数值向下对齐到2的n次幂。
默认的高水线是批量值的6倍。
可以通过文件“/proc/sys/vm/percpu_pagelist_fraction”修改比例值,最小值是8,默认值是0。高水线等于(伙伴分配器管理的页数量 / 比例值),同时把批量值设置为高水线的1/4。
从某个内存区域申请某种迁移类型的单页时,从当前处理器的页集合中该迁移类型的页链表分配页,如果页链表是空的,先批量申请页加入页链表,然后分配一页。
缓存热页是指刚刚访问过物理页,物理页的数据还在处理器的缓存中。如果要申请缓存热页,从页链表首部分配页;如果要申请缓存冷页,从页链表尾部分配页。
释放单页时,把页加入当前处理器的页集合中。如果释放缓存热页,加入页链表首部;如果释放缓存冷页,加入页链表尾部。如果页集合中的页数量大于或等于高水线,那么批量返还给伙伴分配器。