这里讨论冷热缓存相关的数据结构的初始化,以及用于控制缓存填充行为的“水印”的计算。Zone_pcp_init负责初始化该缓存,该函数由free_area_init_nodes调用。
start_kernel
->setup_arch
->machine_specific_memory_setup
->setup_memory
->paging_init
-> free_area_init
->free_area_init_node
->free_area_init_core
->zone_pcp_init
static __meminit void zone_pcp_init(struct zone *zone) { /* * per cpu subsystem is not up at this point. The following code * relies on the ability of the linker to provide the * offset of a (static) per cpu variable into the per cpu area. */ zone->pageset = &boot_pageset; if (populated_zone(zone)) printk(KERN_DEBUG " %s zone: %lu pages, LIFO batch:%u\n", zone->name, zone->present_pages, zone_batchsize(zone)); }
如下是zone_batchsize计算水印的过程:
static int zone_batchsize(struct zone *zone) { #ifdef CONFIG_MMU int batch; batch = zone->managed_pages / 1024; if (batch * PAGE_SIZE > 512 * 1024) batch = (512 * 1024) / PAGE_SIZE; batch /= 4; /* We effectively *= 4 below */ if (batch < 1) batch = 1; batch = rounddown_pow_of_two(batch + batch/2) - 1; return batch; #else return 0; #endif }
上述代码计算得到batch,大约相当于内存域中页数的0.25%o。移位操作确保计算结果具有2^N-1的形式。根据经验,该值在大多数系统负载下都能最小化缓存混叠效应。Fls是一个特定于计算机的操作,用于算出一个值中置位的最低比特位。要注意,这种校正会使结果值偏离内存域中页数的0.25%o.batch = 22时偏差最大,由于22+11-1=32,fls会算出比特位5是最低置位比特位,而1<<5-1=31,通常情况下偏差都比这小,实际上可以忽略。
内存域中的内存数量超过512MB时,批量大小并不增长。对于页面大小为4096KB的系统,如果页数超过13172,则会达到512MB的限制。在setup_pageset中考虑缓存极限的计算时,batch值是有意义的。
setup_pageset
->pageset_init
->pageset_set_batch
->pageset_update
static void setup_pageset(struct per_cpu_pageset *p, unsigned long batch) { pageset_init(p); pageset_set_batch(p, batch); } static void pageset_init(struct per_cpu_pageset *p) { struct per_cpu_pages *pcp; int migratetype; memset(p, 0, sizeof(*p)); pcp = &p->pcp; pcp->count = 0; for (migratetype = 0; migratetype < MIGRATE_PCPTYPES; migratetype++) INIT_LIST_HEAD(&pcp->lists[migratetype]); } static void pageset_set_batch(struct per_cpu_pageset *p, unsigned long batch) { pageset_update(&p->pcp, 6 * batch, max(1UL, 1 * batch)); } static void pageset_update(struct per_cpu_pages *pcp, unsigned long high, unsigned long batch) { /* start with a fail safe value for batch */ pcp->batch = 1; smp_wmb(); /* Update high, then batch, in order */ pcp->high = high; smp_wmb(); pcp->batch = batch; }
对热页来说,下限为0,上限为6*batch,缓存中页的平均数量大约是4*batch,因为内核不会让缓存水平降到太低,batch*4相当于内存域中页数的千分之一(这也是zone_batchsize试图将批量大小优化到总页数0.25%o的原因)。IA-32处理器上L2缓存数量在0.25MB~2MB之间,因此在冷热缓存中保持更多的内存是无意义的。根据经验,缓存大小是主内存的千分之一。考虑到当前系统每个CPU配备的物理内存大约在1GB~2GB,该规则是有意义的。这样,计算出批量大小使得冷热缓存中的也有可能放置到L2缓存中。
冷页列表的水印稍低一些,因为冷页并不放置到缓存中,只用于一些不太关注性能的操作,其上限是batch值的两倍。
Pcp->batch决定了在重新填充列表时,有多少页会立即使用。出于性能方面的考虑,一般会向列表添加连续的多页,而不是单页。
在zone_pcp_init结束时,会输出各个内存域的页数及计算出的批量大小,从启动日志可以看到。
[ 0.000000] Initmem setup node 0 [mem 0x00001000-0x6dffffff]
[ 0.000000] On node 0 totalpages: 447905
[ 0.000000] DMA zone: 32 pages used for memmap
[ 0.000000] DMA zone: 0 pages reserved
[ 0.000000] DMA zone: 3995 pages, LIFO batch:0
[ 0.000000] Normal zone: 1744 pages used for memmap
[ 0.000000] Normal zone: 222974 pages, LIFO batch:31
[ 0.000000] HighMem zone: 1745 pages used for memmap
[ 0.000000] HighMem zone: 220936 pages, LIFO batch:31