Linux内核深度解析
上QQ阅读APP看书,第一时间看更新

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。


从某个内存区域申请某种迁移类型的单页时,从当前处理器的页集合中该迁移类型的页链表分配页,如果页链表是空的,先批量申请页加入页链表,然后分配一页。

缓存热页是指刚刚访问过物理页,物理页的数据还在处理器的缓存中。如果要申请缓存热页,从页链表首部分配页;如果要申请缓存冷页,从页链表尾部分配页。


释放单页时,把页加入当前处理器的页集合中。如果释放缓存热页,加入页链表首部;如果释放缓存冷页,加入页链表尾部。如果页集合中的页数量大于或等于高水线,那么批量返还给伙伴分配器。