linux内核源码分析之物理内存组织结构

目录

体系结构

内存模型

三级结构

1、内存节点(pglist_data)

2、内存区域(zone)

3、物理页(page)

页表


体系结构

目前多处理器系统有两种体系结构:

  1. 非一致内存访问(Non-Unit Memory Access,NUMA):指内存被划分成多个 内存节点的多处理器系统。访问一个内存节点花费的时间取决于处理器和内存节点的距离。
  2. 对称多处理器(Symmetric Multi-Processor,SMP):即一致内存访问 (Uniform Memory Access,UMA),所有处理器访问内存花费的时间是相同。


内存模型

        内存模型是从处理器角度看到的物理内存分布,内核管理不同内存模型的方式存差异。

内存管理子系统支持3种内存模型:

  1. 平坦内存(Flat Memory):内存的物理地址空间是连续的,没有空洞。
  2. 不连续内存(Discontiguous Memory):内存的物理地址空间存在空洞,这种模型可以高效地处理空洞。
  3. 稀疏内存(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:32位时代产物,内核和用户地址空间1:3划分,不能把内核1GB以上的内存直接映射到内核地址空间;64系统为不再需要
  • ZONE_MOVABLE可移动区域,是一个伪内存区域,用来防止内存碎片
  • ZONE_DEVICE:支持持久内存热插拔增加的区域

内存节点被划分为内存区域。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内核深度解析》

  • 10
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 12
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

为了维护世界和平_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值