内存管理-11-buddy伙伴子系统-2-PCP-Per-CPU页缓存
基于msm-5.4
一、概述
1. 实现背景
buddy子系统管理的物理页面,绝大多数都是放在 zone::free_area[] 中的链表中,少部分放在 zone::lowmem_reserve[] 中。还有少量页面放在 zone::__percpu pageset 这个每CPU变量中,每种迁移类型也都对应一个链表,但是没有order,都是单页大小的内存块。
由于前两个部分对所有CPU来说是全局的,链表的维护需要拿锁,这个同步带来开销。现在多核CPU是有多级缓存的,只有L1是私有的,L2/L3都是公有的,不同的CPU去访问同一个变量的时候,又涉及到缓存的同步刷新问题,这也会带来一定的开销。
使用本地缓存的页面不需要去申请锁,单个物理页面的申请释放是最频繁的,如果将单个物理页面的申请与释放做成一个每CPU的缓存,这样就不需要去拿全局锁了。Per-CPU页帧缓存的目的就是提升申请物理内存的性能,减少锁和cache缓存同步带来的开销。
2. 实现原理
struct zone 中有一个每CPU的 pageset 成员。
struct zone { struct per_cpu_pageset __percpu *pageset; } struct per_cpu_pageset { struct per_cpu_pages pcp; s8 stat_threshold; s8 vm_stat_diff[NR_VM_ZONE_STAT_ITEMS]; }; struct per_cpu_pages { int count; //lists链表中页面的个数 int high; //高水位,需要清空 int batch; //伙伴块添加/删除的大小 struct list_head lists[MIGRATE_PCPTYPES]; //每个迁移类型一个链表,链表上的页面都是单个物理页 };
在释放单个物理页的时候,不会释放给伙伴系统,而是直接释放到自己的本地 per_cpu_pages::lists 链表上。等到下次再申请单个物理页面时,首先去本地链表上去拿,若拿不到再到全局伙伴系统中去拿。
这个本地链表上缓存的单个物理页面的个数不是无限增加的,当超过 _watermark[] 水位值,伙伴系统中页面数较少的时候,会再将每CPU的页缓存释放到伙伴系统的全局链表上。
二、Linux-6.1上情况
Linux-5.4 上的 PCP 还只能缓存单页,但是 Linux-6.1 内核上可以缓存 order 0-3(通常) + 特殊的 THP(order=10)。pcp缓存链表的个数一共有 17 个。
1. pcp 允许的 order
/* * order <=3 或 order==10(THP) 返回 true,否则返回 false, pcp可以缓存 2^0--2^3 和 * 2^10大小的页。 * * get_page_from_freelist() --> rmqueue() --> pcp_allowed_order() */ static inline bool pcp_allowed_order(unsigned int order) //page_alloc.c { if (order <= PAGE_ALLOC_COSTLY_ORDER) //3 return true; #ifdef CONFIG_TRANSPARENT_HUGEPAGE //1 if (order == pageblock_order) //10 return true; #endif return false; }
2. 数据结构
//include/linux/mmzone.h #ifdef CONFIG_TRANSPARENT_HUGEPAGE #define NR_PCP_THP 1 #else #define NR_PCP_THP 0 #endif /* pcp list 除了 2^0 上还有 2^1, 2^2, 2^3 大小的页块 */ #define NR_LOWORDER_PCP_LISTS (MIGRATE_PCPTYPES * (PAGE_ALLOC_COSTLY_ORDER + 1)) //4*(3+1) = 16 #define NR_PCP_LISTS (NR_LOWORDER_PCP_LISTS + NR_PCP_THP) //16 + 1 = 17 struct per_cpu_pages { spinlock_t lock; int count; int high; int batch; short free_factor; struct list_head lists[NR_PCP_LISTS]; } ____cacheline_aligned_in_smp;
成员介绍:
lock:
保护 lists[] 的自旋锁(处理并发 drain).
count:
缓存中的总页数(按高阶等价计).
high:
触发释放的高水位.
batch:
补货/释放的批量单位
free_factor:
动态调整释放批量大小(自适应)
lists[]:
lists 数组的组织方式:
lists[0] <-- order-0, MIGRATE_UNMOVABLE lists[1] <-- order-1, MIGRATE_UNMOVABLE lists[2] <-- order-2, MIGRATE_UNMOVABLE lists[3] <-- order-3, MIGRATE_UNMOVABLE lists[4] <-- order-0, MIGRATE_MOVABLE lists[5] <-- order-1, MIGRATE_MOVABLE ... lists[16] <-- THP 专用(如果启用 CONFIG_TRANSPARENT_HUGEPAGE)
判断函数:
static inline unsigned int order_to_pindex(int migratetype, int order) //page_alloc.c { int base = order; #ifdef CONFIG_TRANSPARENT_HUGEPAGE if (order > PAGE_ALLOC_COSTLY_ORDER) { //3 VM_BUG_ON(order != pageblock_order); //10 return NR_LOWORDER_PCP_LISTS; //16, 是下标,表示 lists[16] } #else VM_BUG_ON(order > PAGE_ALLOC_COSTLY_ORDER); #endif return (MIGRATE_PCPTYPES * base) + migratetype; // 4 * order + migratetype, 是数组下标 }
3. 调试节点
(1) /proc/zoneinfo
# cat /proc/zoneinfo ... cpu: 3 count: 3552 //打印 count 成员 high: 4123 //打印 high 成员 batch: 63 //打印 batch 成员 ...
posted on 2024-07-02 14:43 Hello-World3 阅读(182) 评论(0) 收藏 举报
浙公网安备 33010602011771号