linux内核源码分析之物理内存组织结构
Posted 为了维护世界和平_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux内核源码分析之物理内存组织结构相关的知识,希望对你有一定的参考价值。
目录
体系结构
目前多处理器系统有两种体系结构:
- 非一致内存访问(Non-Unit Memory Access,NUMA):指内存被划分成多个 内存节点的多处理器系统。访问一个内存节点花费的时间取决于处理器和内存节点的距离。
- 对称多处理器(Symmetric Multi-Processor,SMP):即一致内存访问 (Uniform Memory Access,UMA),所有处理器访问内存花费的时间是相同。
内存模型
内存模型是从处理器角度看到的物理内存分布,内核管理不同内存模型的方式存差异。
内存管理子系统支持3种内存模型:
- 平坦内存(Flat Memory):内存的物理地址空间是连续的,没有空洞。
- 不连续内存(Discontiguous Memory):内存的物理地址空间存在空洞,这种模型可以高效地处理空洞。
- 稀疏内存(Space Memory):内存的物理地址空间存在空洞,如果要支持内存热 插拔,只能选择稀疏内存模型。
三级结构
内存管理子系统使用节点(node),区域(zone)、页(page)三级结构描述物理内存
1、内存节点(pglist_data)
分为两种情况:
a,NUMA体系的内存节点,根据处理器和内存的距离划分;
b,在具有不连续内存的NUMA系统中,表示比区域的级别更高的内存区域,根据物 理地址是否连续划分,每块物理地址连续的内存是一个内存节点。
typedef struct pglist_data
struct zone node_zones[MAX_NR_ZONES];//内存区域数组
struct zonelist node_zonelists[MAX_ZONELISTS];//备用区域列表
int nr_zones;//该节点包含的内存区域数量
#ifdef CONFIG_FLAT_NODE_MEM_MAP /* means !SPARSEMEM */
struct page *node_mem_map;
#ifdef CONFIG_PAGE_EXTENSION
struct page_ext *node_page_ext;//页的扩展属性
#endif
#endif
unsigned long node_start_pfn;//该节点的起始物理页号
unsigned long node_present_pages; /* 物理页总数total number of physical pages */
unsigned long node_spanned_pages; /* 物理页范围的总长度,包括空洞total size of physical page
range, including holes */
int node_id;//节点标识符
...
2、内存区域(zone)
类型 ZONE_DMA,ZONE_DMA32,ZONE_NORMAL,ZONE_HIGHMEM,ZONE_MOVABLE等
- ZONE_DMA标记适合DMA的内存域。该区域的长度依赖于处理器类型。在IA-32计算机上,一般的限制是16 MiB
- ZONE_DMA32标记了使用32位地址字可寻址、适合DMA的内存域。显然,只有在64位系统上, 两种DMA内存域才有差别。在32位计算机上,本内存域是空的,即长度为0 MiB。在Alpha和 AMD64系统上,该内存域的长度可能从0到4 GiB。
- ZONE_NORMAL标记了可直接映射到内核段的普通内存域。这是在所有体系结构上保证都会存在的唯一内存域,但无法保证该地址范围对应了实际的物理内存。例如,如果AMD64系统有2 GiB 内存,那么所有内存都属于ZONE_DMA32范围,而ZONE_NORMAL则为空。
- ZONE_HIGHMEM标记了超出内核段的物理内存。
内存节点被划分为内存区域。Linux内核源码分析:include/linux/mmzone.h
enum zone_type
//DMA区域,直接内存访问。如果有些设备不能直接访问所有内存,需要使用DMA区域
#ifdef CONFIG_ZONE_DMA
ZONE_DMA,
#endif
//DMA32区域64位系统,如果既要支持只能直接访问16MB以下的内存设备,又要支持只能直接访问4GB以下
//内存的32设备,必须使用此DMA32区域
#ifdef CONFIG_ZONE_DMA32
ZONE_DMA32,
#endif
/*
* Normal addressable memory is in ZONE_NORMAL. DMA operations can be
* performed on pages in ZONE_NORMAL if the DMA devices support
* transfers to all addressable memory.
*/
//普通区域:直接映射到内核虚拟地址空间的内存区域,又称为普通区域
ZONE_NORMAL,
#ifdef CONFIG_HIGHMEM
/*
* A memory area that is only addressable by the kernel through
* mapping portions into its own address space. This is for example
* used by i386 to allow the kernel to address the memory beyond
* 900MB. The kernel will set up special mappings (page
* table entries on i386) for each page that the kernel needs to
* access.
*/
//高端内存区域:内核和用户地址空间按1:3划分,内核地址空间只有1GB,不能把1GB以上的
//内存直接映射到内核地址
ZONE_HIGHMEM,
#endif
//可移动区域:它是一个伪内存区域,用来放在内存碎片
ZONE_MOVABLE,
#ifdef CONFIG_ZONE_DEVICE
//设备区域:支持持久内存热插拔增加的内存区域,每个内存区域有一个zone结构体来描述
ZONE_DEVICE,
#endif
__MAX_NR_ZONES
;
每个内存区域使用一个zone结构体描述
struct zone
/* zone watermarks, access with *_wmark_pages(zone) macros */
unsigned long _watermark[NR_WMARK];//页分配器使用的水线
unsigned long watermark_boost;
unsigned long nr_reserved_highatomic;
long lowmem_reserve[MAX_NR_ZONES];//当前区域预留多少页不能给高位的区域类型
#ifdef CONFIG_NUMA
int node;
#endif
struct pglist_data *zone_pgdat;
struct per_cpu_pageset __percpu *pageset;
...
/* zone_start_pfn == zone_start_paddr >> PAGE_SHIFT */
unsigned long zone_start_pfn;//当前区域的起始
/* 伙伴分配器管理的物理页数量
* managed_pages is present pages managed by the buddy system, which
* is calculated as (reserved_pages includes pages allocated by the
* bootmem allocator):
* managed_pages = present_pages - reserved_pages;
*/
atomic_long_t managed_pages;
/*
* 当前区域跨越的总页数,包括空洞
* spanned_pages is the total pages spanned by the zone, including
* holes, which is calculated as:
* spanned_pages = zone_end_pfn - zone_start_pfn;
*/
unsigned long spanned_pages;
/* 当前区域存在的物理页的数量,不包括空洞
* present_pages is physical pages existing within the zone, which
* is calculated as:
* present_pages = spanned_pages - absent_pages(pages in holes);
*/
unsigned long present_pages;
const char *name;
int initialized;
/* Write-intensive fields used from the page allocator */
ZONE_PADDING(_pad1_)
/* free areas of different sizes */
struct free_area free_area[MAX_ORDER];//不同长度的空间区域
/* zone flags, see below */
unsigned long flags;
/* Primarily protects free_area */
spinlock_t lock;
/* Write-intensive fields used by compaction and vmstats. */
ZONE_PADDING(_pad2_)
...
冷热页
struct zone的pageset成员用于实现冷热分配器。
热页:意味着已经加载到cpu高度缓存,与内存中的页相比,其数据结构能够快速访问。
冷页:不在高速缓存中。
在多处理器系统上每个CPU都有一个或者多个高速缓存,各CPU的管理必须是独立的。
struct per_cpu_pageset __percpu *pageset;
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];
;
count记录了与该列表相关的页的数目,high是一个水印。如果count的值超出了high,则表明
列表中的页太多了。对容量过低的状态没有显式使用水印:如果列表中没有成员,则重新填充。
如有可能,CPU的高速缓存不是用单个页来填充的,而是用多个页组成的块。batch是每次添加页数的一个参考值。
下图说明了在双处理器系统上per-CPU缓存的数据结构式如何填充的。
3、物理页(page)
页是内存管理当中最小单位,页面中的内存其物理地址是连续的。
页,在内核中,内存管理单元MMU把物理页page作为内存管理的基本单位,
不同体系结构,支持的页大小页也不同
32位体系结构支持4kb的页
64位体系结构支持8kb的页
MIPS64架构体系支持16kb的页
每个物理页对应一个page结构体,称为页描述符,内存节点的pglist_data实例的成员node_mem_map指向该内存节点包含的所有物理页的页描述符组成的数组。Linux内核源码分
析:include/linux/mm_types.h
struct page
unsigned long flags;//原子标志,有些情况下会异步更新
union
struct /* Page cache and anonymous pages */
struct list_head lru;
//如果最低位位0,则指向inode address_space 或为NULL
//如果页映射为匿名地址,最低位置位,而且指针指向anon_vma对象
struct address_space *mapping;
pgoff_t index; /* Our offset within mapping. */
//由映射私有,不透明数据;
//如果设置了PagePrivate,通常用于buffer_heads,
//如果设置了PageSwapCache,则用于swp_entry_t
//如果设置了PageBuddy 则用于伙伴系统中的阶
unsigned long private;
;
struct /* slab, slob and slub */
union
struct list_head slab_list;
struct /* Partial pages */
struct page *next;
#ifdef CONFIG_64BIT
int pages; /* Nr of pages left */
int pobjects; /* Approximate count */
#else
short int pages;
short int pobjects;
#endif
;
;
struct kmem_cache *slab_cache; /* not slob */
/* Double-word boundary */
void *freelist; /* first free object */
union
void *s_mem; /* slab: first object */
unsigned long counters; /* SLUB */
struct /* SLUB */
unsigned inuse:16;
unsigned objects:15;
unsigned frozen:1;
;
;
;
...
页表
层次化的页表用于支持对大地址空间的快速、高效的管理。
1. 内存地址的分解
根据四级页表结构的需要,虚拟内存地址分为5部分(4个表项用于选择页,1个索引表示页内位置)。分解虚拟内存地址如下
- 每个指针末端的几个比特位,用于指定所选页帧内部的位置。比特位的具体数目由PAGE_SHIFT 指定。
- PMD_SHIFT指定了页内偏移量和最后一级页表项所需比特位的总数。该值减去PAGE_SHIFT,可得 最后一级页表项索引所需比特位的数目。
-
PUD_SHIFT由 PMD_SHIFT加上中间层页表索引所需的比特位长度,而 PGDIR_SHIFT则 由
PUD_SHIFT加上上层页表索引所需的比特位长度。对全局页目录中的一项所能寻址的部分地址空间长度计算以2为底的对数,即为PGDIR_SHIFT。
2. 页表的格式
上述定义已经确立了页表项的数目,但没有定义其结构,内核提供了4个数据结构(定义在page.h中)来表示页表项的结构。
- pgd_t用于全局页目录项
- pud_t用于上层页目录项
- pmd_t用于中间页目录项
- pte_t用于直接页表项
3. 特定于PTE的信息
最后一级页表中的项不仅包含了指向页的内存位置的指针,还在上述的多余比特位包含了与页有 关的附加信息。
每种体系结构都必须提供两个东西,使得内存管理子系统能够修改pte_t项中额外的比特位,即保存额外的比特位的__pgprot数据类型,以及修改这些比特位的pte_modify函数。
内核还定义了各种函数,用于查询和设置内存页与体系结构相关的状态。
函数 | 描述 |
pte_present | 页在内存中吗 |
pte_read | 可以写入到该页吗 |
pte_write | 可以写入到该页吗 |
pte_exec | 该页中的数据可以作为二进制代码执行吗 |
pte_dirty | 页是脏的吗?其内容是否修改过 |
pte_file | 该页表项属于非线性映射吗 |
pte_young | 访问位(通常是_PAGE_ACCESS)设置了吗 |
pte_rdprotect | 清除该页的读权限 |
pte_wrprotect | 清除该页的写权限 |
pte_exprotect | 清除执行该页中二进制数据的权限 |
pte_mkread | 设置读权限 |
pte_mkwrite | 设置写权限 |
pte_mkexec | 允许执行页的内容 |
页表项的创建和操作
函 数 | 描 述 |
mk_pte | 创建一个页表项。必须将page实例和所需的页访问权限作为参数传递 |
pte_page | 获得页表项描述的页对应的page实例地址 |
pgd_alloc | 分配并初始化可容纳一个完整页表的内存 |
pgd_free | 释放页表占据的内存 |
set_pgd | 设置页表中某项的值 |
所有体系结构都必须实现表中的函数,以便内存管理代码创建和销毁页表。
参考
《深入linux内核架构》
以上是关于linux内核源码分析之物理内存组织结构的主要内容,如果未能解决你的问题,请参考以下文章
Linux 内核 内存管理物理内存组织结构 ④ ( 内存区域 zone 简介 | zone 结构体源码分析 | zone 结构体源码 )
Linux 内核 内存管理物理内存组织结构 ⑤ ( 内存区域 zone 类型简介 | 内存区域类型zone_type 枚举源码分析 | zone_type 枚举源码 )