第2章:物理内存模型
基于 Linux 6.12.38 源码
2.1 内存节点 (Node) 2.1.1 UMA vs NUMA 架构 Linux 内核支持两种主要的物理内存架构模型:
定义 :所有处理器访问所有内存单元的时间相同,内存是统一共享的。
特点 :
单一共享内存空间,所有 CPU 通过相同的内存总线访问内存
统一的内存控制器,无本地/远程内存之分
内存访问延迟对所有 CPU 一致
结构简单,编程模型直观
典型应用 :
单处理器系统
对称多处理器 (SMP) 工作站
小型服务器
嵌入式系统
优缺点 :
✅ 简单的内存访问模型,易于编程
✅ 无需考虑内存局部性优化
❌ 扩展性差,内存总线成为瓶颈
❌ 多 CPU 竞争内存带宽
UMA 架构示意图:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 ┌─────────────────────────────────────────────────────────────┐ │ UMA 架构 (SMP 系统) │ ├─────────────────────────────────────────────────────────────┤ │ │ │ CPU0 CPU1 CPU2 CPU3 │ │ │ │ │ │ │ │ └───────┴───────┴───────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────┐ │ │ │ 统一内存控制器 │ │ │ └─────────┬───────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────┐ │ │ │ │ │ │ │ 物理内存 (统一地址空间) │ │ │ │ │ │ │ │ Node 0 (contig_page_data) │ │ │ │ ├── Zone DMA │ │ │ │ ├── Zone Normal │ │ │ │ └── Zone Movable │ │ │ │ │ │ │ └─────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────┘ 所有 CPU 访问内存的延迟相同
定义 :处理器访问本地内存的速度快于访问远程内存。
特点 :
多个节点 (Node),每个节点包含 CPU 和本地内存
节点间通过互联总线连接 (QPI, HyperTransport, Infinity Fabric 等)
存在本地内存 (快速访问) 和远程内存 (慢速访问)
需要考虑内存局部性优化
典型应用 :
大型服务器 (2 路、4 路、8 路及以上)
高性能计算 (HPC) 集群
虚拟化主机
优缺点 :
✅ 扩展性好,支持大量 CPU 和内存
✅ 每个 CPU 有专属内存带宽
❌ 编程复杂,需考虑内存亲和性
❌ 远程内存访问存在延迟惩罚
NUMA 架构示意图:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 ┌─────────────────────────────────────────────────────────────┐ │ NUMA 架构 (多节点系统) │ ├─────────────────────────────────────────────────────────────┤ │ │ │ Node 0 Node 1 │ │ ┌──────────────────┐ ┌──────────────────┐ │ │ │ CPU0 │ │ CPU2 │ │ │ │ CPU1 │ │ CPU3 │ │ │ │ │ │ │ │ │ │ 本地内存 │◄─────► │ 本地内存 │ │ │ │ (快速访问) │ QPI/HT │ (快速访问) │ │ │ │ │ 互联总线 │ │ │ │ └──────────────────┘ └──────────────────┘ │ │ │ ▲ │ ▲ │ │ └───────┴────────────────────────┴───────┘ │ │ 互联网络 (Interconnect) │ │ │ │ 访问 Node 0 内存: CPU0, CPU1 ≈ 80ns │ │ 访问 Node 1 内存: CPU0, CPU1 ≈ 120-150ns (跨节点) │ │ │ └─────────────────────────────────────────────────────────────┘
Linux 内核中的统一处理 关键设计 :Linux 使用相同的数据结构 (pglist_data) 统一处理 UMA 和 NUMA,通过条件编译实现差异。
内存模型类型 :
1 2 3 4 CONFIG_FLATMEM CONFIG_SPARSEMEM CONFIG_DISCONTIGMEM
内核源码位置 :
mm/memblock.c:100 - UMA 的 contig_page_data 定义
include/linux/mmzone.h:1580 - NODE_DATA() 宏定义
include/linux/numa.h:33 - NUMA 的 node_data[] 声明
2.1.2 pglist_data 结构 位置: include/linux/mmzone.h:1298
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 typedef struct pglist_data { struct zone node_zones [MAX_NR_ZONES ]; struct zonelist node_zonelists [MAX_ZONELISTS ]; int nr_zones; #ifdef CONFIG_FLATMEM struct page *node_mem_map ; #endif #if defined(CONFIG_MEMORY_HOTPLUG) || defined(CONFIG_DEFERRED_STRUCT_PAGE_INIT) spinlock_t node_size_lock; #endif unsigned long node_start_pfn; unsigned long node_present_pages; unsigned long node_spanned_pages; int node_id; wait_queue_head_t kswapd_wait; wait_queue_head_t pfmemalloc_wait; wait_queue_head_t reclaim_wait[NR_VMSCAN_THROTTLE]; atomic_t nr_writeback_throttled; unsigned long nr_reclaim_start; #ifdef CONFIG_MEMORY_HOTPLUG struct mutex kswapd_lock ; #endif struct task_struct *kswapd ; int kswapd_order; enum zone_type kswapd_highest_zoneidx ; int kswapd_failures; #ifdef CONFIG_COMPACTION int kcompactd_max_order; enum zone_type kcompactd_highest_zoneidx ; wait_queue_head_t kcompactd_wait; struct task_struct *kcompactd ; bool proactive_compact_trigger; #endif unsigned long totalreserve_pages; #ifdef CONFIG_NUMA unsigned long min_unmapped_pages; unsigned long min_slab_pages; #endif #ifdef CONFIG_DEFERRED_STRUCT_PAGE_INIT unsigned long first_deferred_pfn; #endif #ifdef CONFIG_TRANSPARENT_HUGEPAGE struct deferred_split deferred_split_queue ; #endif #ifdef CONFIG_NUMA_BALANCING unsigned int nbp_rl_start; unsigned long nbp_rl_nr_cand; unsigned int nbp_threshold; #endif struct lruvec __lruvec ; unsigned long flags; #ifdef CONFIG_LRU_GEN struct lru_gen_mm_walk mm_walk ; struct lru_gen_memcg memcg_lru ; #endif struct per_cpu_nodestat __percpu *per_cpu_nodestats ; atomic_long_t vm_stat[NR_VM_NODE_STAT_ITEMS]; #ifdef CONFIG_NUMA struct memory_tier __rcu *memtier ; #endif #ifdef CONFIG_MEMORY_FAILURE struct memory_failure_stats mf_stats ; #endif } pg_data_t ;
2.1.3 节点状态 1 2 3 4 5 6 7 8 9 enum node_states { N_POSSIBLE, N_ONLINE, N_NORMAL_MEMORY, N_HIGH_MEMORY, N_MEMORY, N_CPU, NR_NODE_STATES };
2.1.4 UMA/NUMA 内核代码实现 UMA 系统的实现 在 UMA 系统中,内核使用单一的 pglist_data 结构来管理所有内存。
**contig_page_data 定义 (mm/memblock.c:100)**:
1 2 3 4 #ifndef CONFIG_NUMA struct pglist_data __refdata contig_page_data ;EXPORT_SYMBOL(contig_page_data); #endif
**UMA 的 NODE_DATA() 宏 (include/linux/mmzone.h:1580)**:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 #ifndef CONFIG_NUMA extern struct pglist_data contig_page_data ;static inline struct pglist_data *NODE_DATA (int nid) { return &contig_page_data; } #endif
关键特性 :
无论传入什么 nid,总是返回 &contig_page_data
只有一个 Node (Node 0)
node_states[N_ONLINE] 只包含 Node 0
NUMA 系统的实现 在 NUMA 系统中,每个节点有独立的 pglist_data 结构。
**node_data 数组声明 (include/linux/numa.h:33)**:
1 2 3 4 5 6 7 8 #ifdef CONFIG_NUMA #include <asm/sparsemem.h> extern struct pglist_data *node_data [];#define NODE_DATA(nid) (node_data[nid]) void __init alloc_node_data (int nid) ;void __init alloc_offline_node_data (int nid) ;
NUMA 的节点初始化 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 void __init alloc_node_data (int nid) { struct pglist_data *pgdat ; pgdat = memblock_alloc_node(sizeof (*pgdat), SMP_CACHE_BYTES, nid); pgdat->node_id = nid; NODE_DATA(nid) = pgdat; }
关键特性 :
node_data[] 是指针数组,每个元素指向一个节点的 pglist_data
NODE_DATA(nid) 返回对应节点的结构
MAX_NUMNODES 由 CONFIG_NODES_SHIFT 决定(默认 0,最大 11)
内存模型详解 Linux 6.12 支持三种内存模型,用于描述物理内存到 struct page 数组的映射:
1. FLATMEM (平坦内存模型)
1 2 3 4 CONFIG_FLATMEM y # CONFIG_SPARSEMEM is not set # CONFIG_FLAT_NODE_MEM_MAP is not set
特点 :
物理内存是连续的 (或近似连续)
使用单一的 mem_map 数组
pfn_to_page(page_nr) 简单的线性映射
实现 :
1 2 3 4 5 #define pfn_to_page(pfn) (mem_map + ((pfn) - ARCH_PFN_OFFSET)) #define page_to_pfn(page) ((unsigned long)((page) - mem_map) + ARCH_PFN_OFFSET) struct page *mem_map ;
适用场景 :UMA 系统、内存连续的简单系统
2. SPARSEMEM (稀疏内存模型)
1 2 3 4 5 CONFIG_SPARSEMEM y CONFIG_SPARSEMEM_EXTREME y CONFIG_SPARSEMEM_VMEMMAP y CONFIG_SPARSEMEM_ALLOC_MEM_MAP_TOPOLOGY n
特点 :
支持内存空洞和不连续内存
内存按 section (通常是 128MB) 划分
只有存在物理内存的 section 才分配 struct page 数组
NUMA 系统的默认模型
数据结构 :
1 2 3 4 5 6 7 8 9 10 11 12 13 struct mem_section { unsigned long section_mem_map; struct page *section_root_page ; };
实现 :
1 2 3 4 5 6 7 8 9 10 11 12 13 #define pfn_to_section(pfn) \ ((pfn) >> PFN_SECTION_SHIFT) #define section_to_mem_map(ms) \ ((struct page *)((ms)->section_mem_map & ~SECTION_MARKED_PRESENT)) #define __pfn_to_page(pfn) \ section_to_mem_map(__pfn_to_section(pfn)) #define __page_to_pfn(pg) \ (__section_to_pfn(__page_to_section(pg)) + \ ((unsigned long)((pg) - section_to_mem_map(__page_to_section(pg)))))
Section 大小 :
1 2 3 4 5 #define PAGES_PER_SECTION (1UL << (SECTION_SIZE_BITS - PAGE_SHIFT))
适用场景 :NUMA 系统、支持内存热插拔的系统、大内存系统
3. DISCONTIGMEM (不连续内存模型)
特点 :
历史遗留模型,用于支持不连续物理内存
已被 SPARSEMEM 完全替代
Linux 6.12 中仍存在但已标记为废弃
节点与内存模型的对应关系 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 ┌─────────────────────────────────────────────────────────────┐ │ 节点与内存模型关系 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ UMA 系统: │ │ ┌─────────────────────────────────────────────────┐ │ │ │ 内存模型: FLATMEM │ │ │ │ 节点数: 1 (Node 0) │ │ │ │ 数据结构: contig_page_data (单一 pglist_data) │ │ │ │ 页面映射: mem_map (单一数组) │ │ │ └─────────────────────────────────────────────────┘ │ │ │ │ NUMA 系统: │ │ ┌─────────────────────────────────────────────────┐ │ │ │ 内存模型: SPARSEMEM │ │ │ │ 节点数: 2+ (Node 0, Node 1, ...) │ │ │ │ 数据结构: node_data[] (pglist_data 指针数组) │ │ │ │ 页面映射: mem_section[][] (二维数组) │ │ │ │ │ │ │ │ Node 0 ──▶ pglist_data ──▶ zones ──▶ pages │ │ │ │ Node 1 ──▶ pglist_data ──▶ zones ──▶ pages │ │ │ └─────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────┘
常用 API 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 static inline struct pglist_data *NODE_DATA (int nid) ;#define node_start_pfn(nid) (NODE_DATA(nid)->node_start_pfn) #define node_present_pages(nid) (NODE_DATA(nid)->node_present_pages) #define node_spanned_pages(nid) (NODE_DATA(nid)->node_spanned_pages) static inline struct zone *node_zone (int nid, enum zone_type zone_type) { return &NODE_DATA(nid)->node_zones[zone_type]; } #define for_each_online_node(node) \ for ((node) = 0; (node) < MAX_NUMNODES; (node)++) \ if (node_online((node))) static inline int node_online (int nid) { return nid >= 0 && nid < MAX_NUMNODES && node_isset(nid, node_states[N_ONLINE]); } static inline int cpu_to_node (int cpu) { return early_cpu_to_node(cpu); } static inline int numa_node_id (void ) { #ifdef CONFIG_USE_PERCPU_NUMA_NODE_ID return __this_cpu_read(numa_node); #else return 0 ; #endif }
2.2 内存区域 (Zone) 2.2.1 Zone 类型 为了解决不同硬件对内存访问的限制,Linux 将每个节点的内存划分为多个区域 (zone)。
位置: include/linux/mmzone.h:723
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 enum zone_type { #ifdef CONFIG_ZONE_DMA ZONE_DMA, #endif #ifdef CONFIG_ZONE_DMA32 ZONE_DMA32, #endif ZONE_NORMAL, #ifdef CONFIG_HIGHMEM ZONE_HIGHMEM, #endif ZONE_MOVABLE, #ifdef CONFIG_ZONE_DEVICE ZONE_DEVICE, #endif __MAX_NR_ZONES };
2.2.2 Zone 结构 位置: include/linux/mmzone.h:818
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 struct zone { unsigned long _watermark[NR_WMARK]; unsigned long watermark_boost; unsigned long nr_reserved_highatomic; unsigned long nr_free_highatomic; long lowmem_reserve[MAX_NR_ZONES]; #ifdef CONFIG_NUMA int node; #endif struct pglist_data *zone_pgdat ; struct per_cpu_pages __percpu *per_cpu_pageset ; struct per_cpu_zonestat __percpu *per_cpu_zonestats ; int pageset_high_min; int pageset_high_max; int pageset_batch; #ifndef CONFIG_SPARSEMEM unsigned long *pageblock_flags; #endif unsigned long zone_start_pfn; atomic_long_t managed_pages; unsigned long spanned_pages; unsigned long present_pages; #ifdef CONFIG_MEMORY_HOTPLUG unsigned long present_early_pages; #endif #ifdef CONFIG_CMA unsigned long cma_pages; #endif const char *name; #ifdef CONFIG_MEMORY_ISOLATION unsigned long nr_isolate_pageblock; #endif #ifdef CONFIG_MEMORY_HOTPLUG seqlock_t span_seqlock; #endif int initialized; CACHELINE_PADDING(_pad1_); struct free_area free_area [NR_PAGE_ORDERS ]; #ifdef CONFIG_UNACCEPTED_MEMORY struct list_head unaccepted_pages ; #endif unsigned long flags; spinlock_t lock; CACHELINE_PADDING(_pad2_); unsigned long percpu_drift_mark; #if defined CONFIG_COMPACTION || defined CONFIG_CMA unsigned long compact_cached_free_pfn; unsigned long compact_cached_migrate_pfn[ASYNC_AND_SYNC]; unsigned long compact_init_migrate_pfn; unsigned long compact_init_free_pfn; #endif #ifdef CONFIG_COMPACTION unsigned int compact_considered; unsigned int compact_defer_shift; int compact_order_failed; #endif #if defined CONFIG_COMPACTION || defined CONFIG_CMA bool compact_blockskip_flush; #endif bool contiguous; CACHELINE_PADDING(_pad3_); atomic_long_t vm_stat[NR_VM_ZONE_STAT_ITEMS]; atomic_long_t vm_numa_event[NR_VM_NUMA_EVENT_ITEMS]; } ____cacheline_internodealigned_in_smp;
2.2.3 内存布局示例 x86_64 (64位系统):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ┌─────────────────────────────────────────────────────────────┐ │ 物理内存 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ Node 0 (正常内存节点) │ │ ├── Zone DMA (0-16MB) - 用于 ISA DMA │ │ ├── Zone DMA32 (16MB-4GB) - 用于 32位 DMA │ │ ├── Zone Normal (4GB-128GB) - 正常内存区域 │ │ └── Zone Movable (可配置) - 可迁移内存 │ │ │ │ Node 1 (NUMA 节点,如果存在) │ │ ├── Zone Normal │ │ └── Zone Movable │ │ │ └─────────────────────────────────────────────────────────────┘
i386 (32位系统):
1 2 3 4 5 6 7 8 9 ┌─────────────────────────────────────────────────────────────┐ │ 物理内存 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ Zone DMA (0-16MB) │ │ Zone Normal (16MB-896MB) │ │ Zone HighMem (896MB-4GB, 需要动态映射) │ │ │ └─────────────────────────────────────────────────────────────┘
2.3 水位 (Watermark) 2.3.1 水位类型 水位是 Linux 内核用于判断是否需要启动页面回收的关键阈值。
位置: include/linux/mmzone.h:647
1 2 3 4 5 6 7 enum zone_watermarks { WMARK_MIN, WMARK_LOW, WMARK_HIGH, WMARK_PROMO, NR_WMARK };
2.3.2 水位计算 1 2 3 4 5 6 7 8 9 10 static inline unsigned long wmark_pages (const struct zone *z, enum zone_watermarks w) { return z->_watermark[w] + z->watermark_boost; } #define min_wmark_pages(z) wmark_pages(z, WMARK_MIN) #define low_wmark_pages(z) wmark_pages(z, WMARK_LOW) #define high_wmark_pages(z) wmark_pages(z, WMARK_HIGH) #define promo_wmark_pages(z) wmark_pages(z, WMARK_PROMO)
2.3.3 水位示意图 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 │ │ min low high promo │ │ │ │ │ │ ▼ ▼ ▼ ▼ │ pages ─ ├───────────────────────────────────────── │ │ ┌─────────┬────────┬─────────┬───────┐ │ │ min │ low │ high │ promo │ │ │ 临界 │ 警告 │ 正常 │ 提升 │ │ └─────────┴────────┴─────────┴───────┘ │ ▼ min : 最小保留页面 (系统紧急使用,GFP_ATOMIC 可用) low : 低于此值唤醒 kswapd 异步回收 high : 低于此值同步回收,直到达到 high promo : 多代 LRU 的提升水位 watermark_boost: 动态提升的水位值
2.3.4 水位相关 API 1 2 3 4 5 6 7 8 bool __zone_watermark_ok(struct zone *z, unsigned int order, unsigned long mark, int highest_zoneidx, unsigned int alloc_flags, long free_pages); bool zone_watermark_ok (struct zone *z, unsigned int order, unsigned long mark, int highest_zoneidx, unsigned int alloc_flags) ;
2.4 物理页面 (Page) 2.4.1 page 结构 位置: include/linux/mm_types.h:72
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 struct page { unsigned long flags; union { struct { union { struct list_head lru ; struct { void *__filler; unsigned int mlock_count; }; struct list_head buddy_list ; struct list_head pcp_list ; }; struct address_space *mapping ; union { pgoff_t index; unsigned long share; }; unsigned long private; }; struct { unsigned long pp_magic; struct page_pool *pp ; unsigned long _pp_mapping_pad; unsigned long dma_addr; atomic_long_t pp_ref_count; }; struct { unsigned long compound_head; }; struct { struct dev_pagemap *pgmap ; void *zone_device_data; }; struct rcu_head rcu_head ; }; union { unsigned int page_type; atomic_t _mapcount; }; atomic_t _refcount; #ifdef CONFIG_MEMCG unsigned long memcg_data; #elif defined(CONFIG_SLAB_OBJ_EXT) unsigned long _unused_slab_obj_exts; #endif #if defined(WANT_PAGE_VIRTUAL) void *virtual; #endif #ifdef LAST_CPUPID_NOT_IN_PAGE_FLAGS int _last_cpupid; #endif } _struct_page_alignment;
2.4.2 页面标志 位置: include/linux/page-flags.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #define PG_locked 0 #define PG_error 1 #define PG_referenced 2 #define PG_uptodate 3 #define PG_dirty 4 #define PG_lru 5 #define PG_active 6 #define PG_slab 7 #define PG_owner_priv_1 8 #define PG_arch_1 9 #define PG_reserved 10 #define PG_private 11 #define PG_writeback 12 #define PG_head 14 #define PG_swapcache 15 #define PG_mappedtodisk 16 #define PG_reclaim 17 #define PG_swapbacked 18 #define PG_unevictable 19
2.5 复合页与 Folio 2.5.1 复合页结构 Linux 使用复合页来管理多个连续的物理页面。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ┌────────────────────────────────────────────────────────────┐ │ 复合页 (order = 2) │ │ 4KB × 4 = 16KB │ ├────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────┬─────────────┬─────────────┬─────────────┐ │ │ │ 页面 0 │ 页面 1 │ 页面 2 │ 页面 3 │ │ │ │ (PG_head) │ (PG_tail) │ (PG_tail) │ (PG_tail) │ │ │ │ │ │ │ │ │ │ │ compound │ │ │ │ │ │ │ _order = 2 │ │ │ │ │ │ │ │ │ │ │ │ │ └─────────────┴─────────────┴─────────────┴─────────────┘ │ │ │ ▲ ▲ ▲ │ │ └───────────┴──────────────┴──────────────┘ │ │ compound_head 指向页面 0 │ └────────────────────────────────────────────────────────────┘
2.5.2 Folio 结构 Linux 5.15+ 引入 folio 结构来更好地管理多页复合页。
位置: include/linux/mm_types.h:324
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 struct folio { union { struct { unsigned long flags; union { struct list_head lru ; struct { void *__filler; unsigned int mlock_count; }; }; struct address_space *mapping ; pgoff_t index; union { void *private; swp_entry_t swap; }; atomic_t _mapcount; atomic_t _refcount; #ifdef CONFIG_MEMCG unsigned long memcg_data; #endif void *virtual; int _last_cpupid; }; struct page page ; }; union { struct { unsigned long _flags_1; unsigned long _head_1; atomic_t _large_mapcount; atomic_t _entire_mapcount; atomic_t _nr_pages_mapped; atomic_t _pincount; #ifdef CONFIG_64BIT unsigned int _folio_nr_pages; #endif }; struct page __page_1 ; }; union { struct { unsigned long _flags_2; unsigned long _head_2; void *_hugetlb_subpool; void *_hugetlb_cgroup; void *_hugetlb_cgroup_rsvd; void *_hugetlb_hwpoison; }; struct { unsigned long _flags_2a; unsigned long _head_2a; struct list_head _deferred_list ; }; struct page __page_2 ; }; };
2.5.3 Folio 操作 API 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 static inline struct folio *page_folio (struct page *page) ;static inline unsigned int folio_nr_pages (struct folio *folio) ;static inline unsigned int folio_order (struct folio *folio) ;struct folio *folio_alloc (gfp_t gfp, unsigned int order) ;struct folio *__folio_alloc_node (gfp_t gfp , unsigned int order , int nid );void folio_put (struct folio *folio) ;
2.6 页面生命周期状态机 2.6.1 页面状态转换 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 ┌─────────────────────────────────────────────────────────────────────┐ │ 页面生命周期状态机 │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ 分配 首次访问 被引用 回收 │ │ │ │ │ │ │ │ ▼ ▼ ▼ ▼ │ │ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │ │ │ FREE │ ───▶ │ NEW │ ─────▶ │ACTIVE│ ───▶ │INACTIVE│ │ │ │ │ 无 │ │ mark_ │ │ 时间 │ │ │ │ │ │ 映射 │ │ page_ │ │ 流逝 │ │ │ │ │ │ │ │ accessed │ │ │ │ │ │ └──────┘ └──────┘ └──────┘ └──────┘ │ │ ▲ │ ▲ │ │ │ │ │ │ ▼ │ │ │ 释放/回收 │ ┌──────┐ │ │ │ │ │ │SWAP │ │ │ └────────────────┴─────────────────┴──────────│ │ │ │ │ cache│ │ │ └──────┘ │ │ │ │ │ ▼ │ │ ┌──────┐ │ │ │ FREE │◀──────┘ │ └──────┘ │ │ │ │ 状态说明: │ │ ──────── │ │ FREE : 在 Buddy System 的 free_area 中 │ │ NEW : 刚分配,未加入 LRU │ │ ACTIVE : 被频繁访问,在 LRU_ACTIVE 链表中 │ │ INACTIVE : 很少访问,在 LRU_INACTIVE 链表中 │ │ SWAP : 被 swap 到磁盘 │ │ │ │ 相关章节: │ │ ───────── │ │ • ch02 - 物理内存模型 (page 结构) │ │ • ch03 - Buddy System (free_area 管理) │ │ • ch07 - 页面分配 (alloc_pages) │ │ • ch10 - 页面回收 (LRU 管理, kswapd) │ │ • ch12 - Swap 机制 │ │ │ └─────────────────────────────────────────────────────────────────────┘
2.6.2 页面状态转换操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 static inline void mark_page_accessed (struct page *page) { if (!PageActive(page) && !PageUnevictable(page) && PageLRU(page)) activate_page(page); } static inline void lru_cache_add_inactive (struct page *page) { if (!PageUnevictable(page)) lru_cache_add(page); } void mark_page_unevictable (struct page *page) ;void clear_page_unevictable (struct page *page) ;void __free_pages(struct page *page, unsigned int order);
2.7 本章小结与跨章节关联 2.7.1 本章要点 本章介绍了 Linux 6.12 的物理内存模型:
UMA vs NUMA 架构 :
UMA (均匀内存访问):单一内存空间,所有 CPU 访问延迟相同,使用 contig_page_data
NUMA (非均匀内存访问):多节点架构,本地内存访问快于远程,使用 node_data[] 数组
**内存节点 (Node)**:pglist_data 结构,通过 NODE_DATA(nid) 访问
内存模型 :
FLATMEM:平坦内存模型,UMA 系统使用
SPARSEMEM:稀疏内存模型,NUMA 系统使用,支持内存热插拔
DISCONTIGMEM:已废弃
**内存区域 (Zone)**:解决硬件访问限制,DMA、Normal、HighMem、Movable、Device
**水位 (Watermark)**:min、low、high、promo 四个水位控制页面回收
**物理页面 (Page)**:struct page 描述符,flags、refcount、mapcount
复合页与 Folio :多页管理,folio 是 Linux 5.15+ 的新抽象
页面生命周期 :从分配到回收的完整状态转换
2.7.2 与其他章节的关系 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ┌────────────────────────────────────────────────────────────┐ │ 本章内容 (物理内存模型) │ ├────────────────────────────────────────────────────────────┤ │ │ │ Node/Zone/Page ──▶ ch03:Buddy System分配器 │ │ (如何使用这些结构分配内存) │ │ │ │ Page 结构 ──────▶ ch07:页面分配 (alloc_pages返回什么) │ │ │ │ watermark ──────▶ ch10:页面回收 (何时触发回收) │ │ │ │ zone ────────────▶ ch04:虚拟地址空间 (如何映射到zone) │ │ │ │ folio ──────────▶ ch12:页缓存 (folio在缓存中的使用) │ │ │ │ 页面生命周期 ────▶ ch09:缺页处理 (页面状态的触发) │ │ │ └────────────────────────────────────────────────────────────┘
关键关联路径:
分配路径 : ch02(物理内存模型) → ch03(Buddy分配器) → ch07(alloc_pages API)
回收路径 : ch02(watermark检查) → ch10(kswapd/直接回收) → ch02(FREE状态)
映射路径 : ch02(Page结构) → ch05(页表管理) → ch06(VMA) → ch09(缺页处理)
下一章将介绍物理内存分配器 (Buddy System)。