前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >物理内存是如何组织管理的

物理内存是如何组织管理的

作者头像
DragonKingZhu
发布于 2020-04-16 08:50:45
发布于 2020-04-16 08:50:45
1.6K00
代码可运行
举报
运行总次数:0
代码可运行

内存管理,相比大家都听过。但是内存管理到底是做什么呢?这就得从计算机刚出来的时候说起。计算机刚出来的时候内存资源很紧张,只有几十K,后来慢慢的到几百K,到周后来的512M,再到现在的几个G。真是因为内存资源的不足,在计算机的整个过程中衍生出各种各样的内存管理方法。

而内存管理的终极目标就是合理的不浪费的使用物理内存。Linux针对如何合理的使用物理内存,软件上设计了多种的内存管理方法。今天我们就来讨论下Linux是如何组织物理内存的,通俗的说就是如何管理电脑的内存条的。

Linux使用节点(node),区域(zone),页(page)三级结构来描述整个物理内存。

node

目前计算机系统有两种体系结构:

  • 非一致性内存访问 NUMA(Non-Uniform Memory Access)意思是内存被划分为各个node,访问一个node花费的时间取决于CPU离这个node的距离。每一个cpu内部有一个本地的node,访问本地node时间比访问其他node的速度快
  • 一致性内存访问 UMA(Uniform Memory Access)也可以称为SMP(Symmetric Multi-Process)对称多处理器。意思是所有的处理器访问内存花费的时间是一样的。也可以理解整个内存只有一个node。
  • NUMA通常用在服务器领域,可以通过CONFIG_NUMA来配置是否开启

zone

ZONE的意思是把整个物理内存划分为几个区域,每个区域有特殊的含义。

先来看下内核中对zone的定义

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
enum zone_type {
#ifdef CONFIG_ZONE_DMA
	/*
	 * ZONE_DMA is used when there are devices that are not able
	 * to do DMA to all of addressable memory (ZONE_NORMAL). Then we
	 * carve out the portion of memory that is needed for these devices.
	 * The range is arch specific.
	 *
	 * Some examples
	 *
	 * Architecture		Limit
	 * ---------------------------
	 * parisc, ia64, sparc	<4G
	 * s390			<2G
	 * arm			Various
	 * alpha		Unlimited or 0-16MB.
	 *
	 * i386, x86_64 and multiple other arches
	 * 			<16M.
	 */
	ZONE_DMA,
#endif
#ifdef CONFIG_ZONE_DMA32
	/*
	 * x86_64 needs two ZONE_DMAs because it supports devices that are
	 * only able to do DMA to the lower 16M but also 32 bit devices that
	 * can only do DMA areas below 4G.
	 */
	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.
	 */
	ZONE_HIGHMEM,
#endif
	ZONE_MOVABLE,
#ifdef CONFIG_ZONE_DEVICE
	ZONE_DEVICE,
#endif
	__MAX_NR_ZONES

};

为了更好的解释各个ZONE的含义,比如上图。

32位系统:

在32位系统中,假设我们物理内存是4G的。

  • DMA_ZONE是因为在X86架构下,有些DMA设备只能访问16M以下的地址,所以设计出了DMA_ZONE,当DMA设备访问内存时,从DMA_ZONE去获取内存
  • HIGHMEM_ZONE: HIGHMEM_ZONE是32位时代的产物。出现的原因是:32位系统中4G的虚拟地址空间划分为0-3G是用户空间,3-4G是内核空间。而内核为了方便操作,需要将物理地址和内核虚拟空间建立线性映射的,而就因为内核只有1G空间,而物理内存有4G,是完全不能够线性映射的。这时候就将内核3G-3G+896M的地址线性映射到物理内存0-896M的区域。而896-4G的不能映射的区域就叫highmem_zone了。此处896是经典的x86架构的值,arm架构的值没研究。
  • NORAML_ZONE: 16M-896M的区域就称为NORAML_ZONE了。
  • 通常将HIGHMEM_ZONE的内存区域称为高端内存,896M以下的内存称为低端内存,低端内存是线性映射的

可以看看我的32位ubuntu机器,存在Noraml zone,DMA zone,HighMem zone。

64位系统

  • 在64位系统上因为虚拟地址空间已经足够大了。比如当地址宽度的位数是39位的时候。用户空间和内核空间大小是一样大,大小是512G。
  • 假设此时物理内存是4G,则整个4G都可以全部映射到内核虚拟地址区间的。所以说64位机器上已经不存在HIGHMEM_ZONE了。
  • 而在x86的64位机器上还可能存在DMA,DMA_32的区域,用于DMA传输使用。
  • 比如我的ubuntu机器,可以通过/proc/buddinfo看具体zone的信息
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
root@root-OptiPlex-7060:~$ cat /proc/buddyinfo 
Node 0, zone      DMA      3      3      1      1      3      2      0      0      1      1      3 
Node 0, zone    DMA32   4053    729    155    166    105     43    151      0      0      0      0 
Node 0, zone   Normal  33893   8921   6356   1472   1221    101     48     10      4      0      0 

再比如看下我的一台ARm64的手机。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
root:/ # cat /proc/buddyinfo
Node 0, zone   Normal     12      7    148     52    114     39     16      8      5      5    117
Node 0, zone  Movable    470   1135    880    340     35      8      4      2      3      0    653

可以看到在64位机器上已经不存在HIGHMEM_ZONE了。只剩下一个NORAML_ZONE

ZONE_MOVABLE:用于内存碎片技术,意思就是当内存出现碎片的时候,为了调整出一个大得连续内存的时候,就需要将Moveablezone的内容做交换,换出一个大得连续的内存区域。

page

就是代表一个物理页,一个物理页用一个struct page在内核中表示。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
struct page {
{
	unsigned long flags;		/* Atomic flags, some possibly
					 * updated asynchronously */
	/*
	 * Five words (20/40 bytes) are available in this union.
	 * WARNING: bit 0 of the first word is used for PageTail(). That
	 * means the other users of this union MUST NOT use the bit to
	 * avoid collision and false-positive PageTail().
	 */
	union {
		struct {	/* Page cache and anonymous pages */
			/**
			 * @lru: Pageout list, eg. active_list protected by
			 * zone_lru_lock.  Sometimes used as a generic list
			 * by the page owner.
			 */
			struct list_head lru;
			/* See page-flags.h for PAGE_MAPPING_FLAGS */
			struct address_space *mapping;
			pgoff_t index;		/* Our offset within mapping. */
			/**
			 * @private: Mapping-private opaque data.
			 * Usually used for buffer_heads if PagePrivate.
			 * Used for swp_entry_t if PageSwapCache.
			 * Indicates order in the buddy system if PageBuddy.
			 */
			unsigned long private;
		};
		struct {	/* slab, slob and slub */
			union {
				struct list_head slab_list;	/* uses lru */
				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;
				};
			};
		};
		struct {	/* Tail pages of compound page */
			unsigned long compound_head;	/* Bit zero is set */

			/* First tail page only */
			unsigned char compound_dtor;
			unsigned char compound_order;
			atomic_t compound_mapcount;
		};
		struct {	/* Second tail page of compound page */
			unsigned long _compound_pad_1;	/* compound_head */
			unsigned long _compound_pad_2;
			struct list_head deferred_list;
		};
		struct {	/* Page table pages */
			unsigned long _pt_pad_1;	/* compound_head */
			pgtable_t pmd_huge_pte; /* protected by page->ptl */
			unsigned long _pt_pad_2;	/* mapping */
			union {
				struct mm_struct *pt_mm; /* x86 pgds only */
				atomic_t pt_frag_refcount; /* powerpc */
			};
#if ALLOC_SPLIT_PTLOCKS
			spinlock_t *ptl;
#else
			spinlock_t ptl;
#endif
		};
		struct {	/* ZONE_DEVICE pages */
			/** @pgmap: Points to the hosting device page map. */
			struct dev_pagemap *pgmap;
			unsigned long hmm_data;
			unsigned long _zd_pad_1;	/* uses mapping */
		};

		/** @rcu_head: You can use this to free a page by RCU. */
		struct rcu_head rcu_head;
	};

	union {		/* This union is 4 bytes in size. */
		/*
		 * If the page can be mapped to userspace, encodes the number
		 * of times this page is referenced by a page table.
		 */
		atomic_t _mapcount;

		/*
		 * If the page is neither PageSlab nor mappable to userspace,
		 * the value stored here may help determine what this page
		 * is used for.  See page-flags.h for a list of page types
		 * which are currently stored here.
		 */
		unsigned int page_type;

		unsigned int active;		/* SLAB */
		int units;			/* SLOB */
	};

	/* Usage count. *DO NOT USE DIRECTLY*. See page_ref.h */
	atomic_t _refcount;

#ifdef CONFIG_MEMCG
	struct mem_cgroup *mem_cgroup;
#endif

	/*
	 * On machines where all RAM is mapped into kernel address space,
	 * we can simply calculate the virtual address. On machines with
	 * highmem some memory is mapped into kernel virtual memory
	 * dynamically, so we need a place to store that address.
	 * Note that this field could be 16 bits on x86 ... ;)
	 *
	 * Architectures with slow multiplication can define
	 * WANT_PAGE_VIRTUAL in asm/page.h
	 */
#if defined(WANT_PAGE_VIRTUAL)
	void *virtual;			/* Kernel virtual address (NULL if
					   not kmapped, ie. highmem) */
#endif /* WANT_PAGE_VIRTUAL */

#ifdef LAST_CPUPID_NOT_IN_PAGE_FLAGS
	int _last_cpupid;
#endif
}

可以看到struct page结构体里面基本都是联合体,就是为了节省空间。因为物理页很多 ,则为了表示物理页就需要很多的page,而page是需要占用内存的。所以page结构体采用了联合体这种结构来组织。但是可读性很差。

Page Frame

为了描述一个物理page,内核使用struct page结构来表示一个物理页。假设一个page的大小是4K的,内核会将整个物理内存分割成一个一个4K大小的物理页,而4K大小物理页的区域我们称为page frame

Page Frame Num(PFN)

将物理地址分成一块一块的大小,就比如大小是4K的话,将一个物理页的区域我们称为page frame, 而对每个page frame的编号就称为PFN.

物理地址和pfn的关系是:物理地址>>PAGE_SHIFT = pfn

pfn和page的关系:

内核中支持了好几个内存模型:CONFIG_FLATMEM(平坦内存模型)CONFIG_DISCONTIGMEM(不连续内存模型)CONFIG_SPARSEMEM_VMEMMAP(稀疏的内存模型)目前ARM64使用的稀疏的类型模式

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/* memmap is virtually contiguous.  */
#define __pfn_to_page(pfn)	(vmemmap + (pfn))
#define __page_to_pfn(page)	(unsigned long)((page) - vmemmap)

刚开机的时候,内核会将整个struct page映射到内核虚拟地址空间vmemmap的区域,所以我们可以简单的认为struct page的基地址是vmemmap,则:

vmemmap+pfn的地址就是此struct page对应的地址.

总结:

一个物理内存分为好几个node,每个node存在好几个zone,每个zone中细分为page大小。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
一步一图带你深入理解 Linux 物理内存管理
在上篇文章 《深入理解 Linux 虚拟内存管理》 中,笔者分别从进程用户态和内核态的角度详细深入地为大家介绍了 Linux 内核如何对进程虚拟内存空间进行布局以及管理的相关实现。在我们深入理解了虚拟内存之后,那么何不顺带着也探秘一下物理内存的管理呢?
bin的技术小屋
2023/10/30
1.7K0
一步一图带你深入理解 Linux 物理内存管理
Linux内存管理
谈到内存管理,最先想到的就是分段和分页机制。计算机刚出现的时候,并没有这些,刚开始是直接使用的物理地址,也就是代码中操作的地址是可以直接和物理地址对应上的,可是后来随着多进程调度的需求,以及有限的物理内存,于是人们就开始做规定,比如对于一块内存,某个范围是属于内核,然后另外一个范围属于进程A,再另外一个范围属于进程B,如下图所示
一只小虾米
2023/03/19
14.2K0
Linux内存管理
五万字 | 深入理解Linux内存管理
作者简介: 程磊,一线码农,在某手机公司担任系统开发工程师,日常喜欢研究内核基本原理。 1.1 内存管理的意义 1.2 原始内存管理 1.3 分段内存管理 1.4 分页内存管理 1.5 内存管理的目标 1.6 Linux内存管理体系 2.1 物理内存节点 2.2 物理内存区域 2.3 物理内存页面 2.4 物理内存模型 2.5 三级区划关系 3.1 Buddy System 3.1.1 伙伴系统的内存来源 3.1.2 伙伴系统的管理数据结构 3.1.3 伙伴系统的算法逻辑 3.1.4 伙伴系统的接口 3.1
刘盼
2022/08/26
4.2K0
五万字 | 深入理解Linux内存管理
一文掌握 Linux 内存管理
作者:dengxuanshi,腾讯 IEG 后台开发工程师 以下源代码来自 linux-5.10.3 内核代码,主要以 x86-32 为例。 Linux 内存管理是一个很复杂的“工程”,它不仅仅是对物理内存的管理,也涉及到虚拟内存管理、内存交换和内存回收等 物理内存的探测 Linux 内核通过 detect_memory()函数实现对物理内存的探测 void detect_memory(void) {  detect_memory_e820();  detect_memory_e801();  d
腾讯技术工程官方号
2021/05/13
2.3K0
Linux内存初始化(下)
我们接着看linux初始化内存的下半部分,等内存初始化后就可以进入真正的内存管理了,初始化我总结了一下,大体分为三步:
刘盼
2020/06/29
3.3K0
内存管理专栏 | 之内存管理架构
一、内存管理架构 二、虚拟地址空间布局架构 三、物理内存体系架构 四、内存结构 五、内存模型 六、虚拟地址和物理地址的转换 七、内存映射原理分析 一、内存管理架构 内存管理子系统架构可以分为:用户空间、内核空间及硬件部分3个层面,具体结构如下所示:1、用户空间:应用程序使用malloc()申请内存资源/free()释放内存资源。2、内核空间:内核总是驻留在内存中,是操作系统的一部分。内核空间为内核保留,不允许应用程序读写该区域的内容或直接调用内核代码定义的函数。3、硬件:处理器包含一个内存管理单元(Memo
刘盼
2022/09/02
1.6K0
内存管理专栏 | 之内存管理架构
好技能 | 操作系统物理内存的组织形式
在阅读这篇文章前,推荐一篇“好”文章:千万级电商线上无阻塞双buffer缓冲优化ID生成机制深度解析。
穿过生命散发芬芳
2024/12/01
1340
Linux内存描述之内存页面page--Linux内存管理(四)
分页单元可以实现把线性地址转换为物理地址, 为了效率起见, 线性地址被分为固定长度为单位的组, 称为”页”, 页内部的线性地址被映射到连续的物理地址. 这样内核可以指定一个页的物理地址和其存储权限, 而不用指定页所包含的全部线性地址的存储权限.
233333
2018/12/19
9.1K0
页框分配器【转】
也就是我们实际中编码时遇到的内存地址并不是对应于实际内存上的地址,我们编码中使用的地址是一个逻辑地址,会通过分段和分页这两个机制把它转为物理地址。而由于linux使用的分段机制有限,可以认为,linux下的逻辑地址=线性地址。也就是,我们编码使用的是线性地址,之后只需要经过一个分页机制就可以把这个地址转为物理地址了。所以我们更重要的可能是去说明一下linux的分页模型。
233333
2019/05/25
7250
Linux-3.14.12内存管理笔记【构建内存管理框架(5)】
前面已经分析了内存管理框架的构建实现过程,有部分内容未完全呈现出来,这里主要做个补充。
233333
2019/10/08
6730
Linux-3.14.12内存管理笔记【构建内存管理框架(5)】
Linux | 内存管理之mmap函数
~/Downloads/research/linux-5.15.4/include/linux/mm_types.h
heidsoft
2023/03/18
1.7K0
Linux | 内存管理之mmap函数
Linux内存描述之内存区域zone--Linux内存管理(三)
为了支持NUMA模型,也即CPU对不同内存单元的访问时间可能不同,此时系统的物理内存被划分为几个节点(node), 一个node对应一个内存簇bank,即每个内存簇被认为是一个节点
233333
2018/12/17
10K1
Linux内存描述之内存区域zone--Linux内存管理(三)
【Linux 内核 内存管理】物理内存组织结构 ⑤ ( 内存区域 zone 类型简介 | 内存区域类型zone_type 枚举源码分析 | zone_type 枚举源码 )
" 内存区域 " 的类型 在 Linux 内核中使用 enum zone_type 枚举类型进行描述 , zone_type 枚举定义在 Linux 内核源码的 linux-4.12\include\linux\mmzone.h#293 位置 ;
韩曙亮
2023/03/30
2.3K0
【Linux 内核 内存管理】物理内存组织结构 ⑤ ( 内存区域 zone 类型简介 | 内存区域类型zone_type 枚举源码分析 | zone_type 枚举源码 )
Linux-3.14.12内存管理笔记【构建内存管理框架(1)】
传统的计算机结构中,整个物理内存都是一条线上的,CPU访问整个内存空间所需要的时间都是相同的。这种内存结构被称之为UMA(Uniform Memory Architecture,一致存储结构)。但是随着计算机的发展,一些新型的服务器结构中,尤其是多CPU的情况下,物理内存空间的访问就难以控制所需的时间相同了。在多CPU的环境下,系统只有一条总线,有多个CPU都链接到上面,而且每个CPU都有自己本地的物理内存空间,但是也可以通过总线去访问别的CPU物理内存空间,同时也存在着一些多CPU都可以共同访问的公共物理内存空间。于是乎这就出现了一个新的情况,由于各种物理内存空间所处的位置不同,于是访问它们的时间长短也就各异,没法保证一致。对于这种情况的内存结构,被称之为NUMA(Non-Uniform Memory Architecture,非一致存储结构)。事实上也没有完全的UMA,比如常见的单CPU电脑,RAM、ROM等物理存储空间的访问时间并非一致的,只是纯粹对RAM而言,是UMA的。此外还有一种称之为MPP的结构(Massive Parallel Processing,大规模并行处理系统),是由多个SMP服务器通过一定的节点互联网络进行连接,协同工作,完成相同的任务。从外界使用者看来,它是一个服务器系统。
233333
2019/10/08
9040
Linux-3.14.12内存管理笔记【构建内存管理框架(1)】
Buddy 内存管理机制(上)
内存是计算机系统中最重要的核心资源之一,Buddy 系统是 Linux 最底层的内存管理机制,它使用 Page 粒度来管理内存。通常情况下一个 Page 的大小为 4K,在 Buddy 系统中分配、释放、回收的最小单位都是 Page。
刘盼
2023/01/05
1.8K0
Buddy 内存管理机制(上)
启动期间的内存管理之bootmem_init初始化内存管理–Linux内存管理(十二)
1. 启动过程中的内存初始化 首先我们来看看start_kernel是如何初始化系统的, start_kerne定义在init/main.c?v=4.7, line 479 其代码很复杂, 我们只截取
233333
2018/12/24
2.9K0
【Linux 内核 内存管理】物理内存组织结构 ③ ( 内存管理系统三级结构 | 内存节点描述 | 内存节点 pglist_data 结构体 | pglist_data 结构体源码 )
Linux 内核中 , 内存节点 ( Node ) 是 " 内存管理 " 的 最顶层的结构 , 下层分别是 区域 和 页 ;
韩曙亮
2023/03/30
1.9K0
【Linux 内核 内存管理】物理内存组织结构 ③ ( 内存管理系统三级结构 | 内存节点描述 | 内存节点 pglist_data 结构体 | pglist_data 结构体源码 )
内存管理相关数据结构之pg_data_t
若系统节点多余一个,则内核会维护一个位图用于提供每个节点的状态信息,状态信息为一个enum。定义如下:
233333
2020/04/13
8310
Linux内存描述之内存节点node--Linux内存管理(二)
这点前面是说的很明白了, NUMA结构下, 每个处理器CPU与一个本地内存直接相连, 而不同处理器之前则通过总线进行进一步的连接, 因此相对于任何一个CPU访问本地内存的速度比访问远程内存的速度要快
233333
2018/11/21
8.5K0
【Linux 内核 内存管理】物理内存组织结构 ⑥ ( 物理页 page 简介 | 物理页 page 与 MMU 内存管理单元 | 内存节点 pglist_data 与 物理页 page 联系 )
在 Linux 内核中 , MMU 内存管理单元 , 主要作用是 将 " 虚拟地址 " 映射到 真实的 " 物理地址 " 中 ,
韩曙亮
2023/03/30
7K0
【Linux 内核 内存管理】物理内存组织结构 ⑥ ( 物理页 page 简介 | 物理页 page 与 MMU 内存管理单元 | 内存节点 pglist_data 与 物理页 page 联系 )
推荐阅读
相关推荐
一步一图带你深入理解 Linux 物理内存管理
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验