
定义
PCP:per_cpu_pages,Buddy system机制中,优先分配最近被释放的页,因为这些页位于cache中的概率比较大。
因此buddy system为每个cpu维护了一个per_cpu_pages数据结构,用于保存最近被释放的页框。
pcp只包含了order <= PAGE_ALLOC_COSTLY_ORDER的页框,在当前内核版本中该值被定义为3。
数据结构
定义在include/linux/mmzone.h
1. per_cpu_pages:

- count:当前链表里page的个数。
- high:高水位。当pcp中page num高于high watermark时就会回收page到buddy。
- batch:每次回收/分配buddy system页表的数量为batch* 2 ^order个page。
- lists: pcp下包含的page的链表,和buddy一样每个migratetype有一个。
- page链表的数量 = migratetype的数量 x pcp支持的order数量,
NR_PCP_LISTS = MIGRATE_PCPTYPES * (PAGE_ALLOC_COSTLY_ORDER + 1)
若pcp包含的页框数量太多,有些页框可能早已不在cache中,从而失去了其本身的目的。这些缓存在pcp中的页框,若释放到free_area中则可能可以与其它页框合并成order更高的内存块,从而减少系统的内存碎片。
high参数目的:
- high:当pcp中的内存高于high时,其会将一部分内存释放回buddy中,如果pcp中的内存不足,也会从free_area中分配部分内存。动态调整范围在 [high_min, high_max] 之间
- 那么每次释放或分配内存的数量是多少呢?它是由batch参数指定的,其表示单次分配或释放的页框数为batch* 2 ^order个。
- high_min ≤ high ≤ high_max
- high_min:通常是batch*4, 保证PCP至少有high_min个page
- high_max:通常是batch * 12, 限制PCP缓存的最大页数。
2. per_cpu_pageset
每个zone下包含有一个per_cpu_pagesett数据结构记录pcp相关信息。

per_cpu_pageset的数据结构如下

内核启动时候start_kernel()调用setup_per_cpu_pageset()初始化per_cpu_pageset:
2.1 setup_per_cpu_pageset

(1)为每个zone调用setup_zone_pageset分配并初始化per_cpu_pageset。
2.2 setup_zone_pageset

(1)给每个cpu分配一块struct per_cpu_pages空间。
PS: alloca_percpu: 为每个 CPU 分配一个独立的内存块。每个 CPU 访问自己的内存副本,无需同步。
(2)初始化每个cpu的struct per_cpu_pages的每个变量, 这里是给每个变量都赋0。
PS: 通过per_cpu_ptr(zone->per_cpu_pageset, cpu_x); 得到一个指针,指向cpu_x的那个struct per_cpu_pages空间。以此来访问这个结构体。
(3)真正的PCP初始化。如果配置了percpu_pagelist_fraction, 将根据实际内存大小计算high和batch的值并赋值。
函数实现
内存分配

调用过程

rmqueue_pcplist

(1)当pcp list为空时调用rmqueue_bulk从buddy申请batch个页面。
(2)从pcp list里找到page分配并从list里删除。
释放内存

free_frozen_page_commit

(1)把page添加到pcp list中,增加pcp的page计数pcp->count。
(2)计算watermark,如果pcp中的page技术高于watermark就释放batch* 2 ^order个page到buddy。

959

被折叠的 条评论
为什么被折叠?



