##pcp 初始化
所謂的pcp是每cpu頁框高速緩衝,由數據結構struct per_cpu_pageset描述,包含在內存域struct zone中。在內核中,系統會經常請求和釋放單個頁框,如果每個cpu高速緩存包含一些預先分配的頁框,被用於滿足本地cpu發出的單一內存請求,就能提升系統的性能。
實際上這裏爲每個內存域(zone)和每個cpu提供了兩個高速緩存:一個熱高速緩存,它存放的頁框中所包含的內存很可能就在cpu硬件高速緩衝。還有一個冷高速緩存。這樣原因是如果內核或用戶態進程在剛分配到頁框後就立即想頁框寫,那麼從熱高速緩存中獲得頁框就對系統性能有利。但如果頁框將被DMA操作填充,那麼從冷高速緩存中獲得頁框是方便的。在這種情況下,不會設計到cpu,並且硬件高速緩存的行不會被修改,從冷高速緩衝獲得頁框爲其他類型的內存分配保存了熱頁框儲備。在現在的內核中,pcp冷熱緩存是維持在一個鏈表中的。
struct per_cpu_pages {
int count; /* number of pages in the list */
int high; /* high watermark, emptying needed */
int batch; /* chunk size for buddy add/remove */
/* Lists of pages, one per migrate type stored on the pcp-lists */
struct list_head lists[MIGRATE_PCPTYPES];
};
struct per_cpu_pageset {
struct per_cpu_pages pcp;
...
};
strcut zone {
...
struct per_cpu_pageset __percpu *pageset; /* __percpu 每個cpu中都有一個pageset */
...
};
在初始化pcp時,新版本的內核與經典的2.6大有不同。在2.6中,pcp初始化工作是由free_area_init_core()->zone_pcp_init()承擔。但在4.4中,已不與內存域zone同時初始化,由start_kernel()->setup_per_cpu_pageset()單獨初始化。在源碼中有清楚的解釋:
/*
* Allocate per cpu pagesets and initialize them.
* Before this call only boot pagesets were available.
*/
void __init setup_per_cpu_pageset(void)
{
struct zone *zone;
for_each_populated_zone(zone)
setup_zone_pageset(zone);
}
setup_zone_pageset 和 zone_pageset_init 共同完成對每個cpu每個zone的pageset遍歷,利用pageset_init()初始化pcp鏈表,和利用pageset_set_high_and_batch爲每個pageset計算每次在高速緩存中將要添加或被刪去的頁框個數。
static void __meminit setup_zone_pageset(struct zone *zone)
{
int cpu;
zone->pageset = alloc_percpu(struct per_cpu_pageset);
for_each_possible_cpu(cpu)
zone_pageset_init(zone, cpu);
}
static void __meminit zone_pageset_init(struct zone *zone, int cpu)
{
struct per_cpu_pageset *pcp = per_cpu_ptr(zone->pageset, cpu);
pageset_init(pcp);
pageset_set_high_and_batch(zone, pcp);
}