化学显存组织体系结构
目前多处理器系统有两种体系结构。
(1)非一致显存访问(Non-UniformMemoryAccesslinux mint,NUMA):指显存被界定成多个显存节点的多处理器系统,访问一个显存节点耗费的时间取决于处理器和显存节点的距离。每位处理器有一个本地显存节点,处理器访问本地显存节点的速率比访问其他显存节点的速率快。NUMA是中高档服务器的主流体系结构。
(2)对称多处理器(SymmetricMulti-Processor,SMP):即一致显存访问(UniformMemoryAccess,UMA),所有处理器访问显存耗费的时间是相同的。每位处理器的地位是平等的,仅在内核初始化的时侯不平等:“0号处理器作为引导处理器负责初始化内核,其他处理器等待内核初始化完成。”
在实际应用中可以采用混和体系结构,在NUMA节点内部使用SMP体系结构。
【文章福利】小编推荐自己的Linux内核源码交流群:【869634926】整理了一些个人认为比较好的学习书籍、视频资料共享在群文件上面,有须要的可以自行添加哦!!!前50名可进群发放,并额外附赠一份价值600的内核资料包(含视频教程、电子书、实战项目及代码)!
点击下方链接即可免费发放内核相关学习资料哦
学习直通车:Linux内核源码/显存调优/文件系统/进程管理/设备驱动/网路合同栈
显存模型
显存模型是从处理器的角度听到的数学显存分布情况,内核管理不同显存模型的形式存在差别。显存管理子系统支持3种显存模型。
(1)平坦显存(FlatMemory):显存的化学地址空间是连续的雨林木风linux,没有空洞。
(2)不连续显存(DiscontiguousMemory):显存的化学地址空间存在空洞,这些模型可以高效地处理空洞。
(3)稀疏显存(SparseMemory):显存的化学地址空间存在空洞。假如要支持显存热拔插,只能选择稀疏显存模型。
哪些情况会出现显存的化学地址空间存在空洞?系统包含多块化学显存,两块显存的化学地址空间之间存在空洞。一块显存的化学地址空间也可能存在空洞,可以查看处理器的参考指南获取分配给显存的化学地址空间。
假如显存的化学地址空间是连续的,不连续显存模型会形成额外的开支linux内核占用cpu高,增加性能linux内核占用cpu高,所以平坦显存模型是更好的选择。
假如显存的化学地址空间存在空洞,应当选择哪种显存模型?
平坦显存模型会为空洞分配page结构体,浪费显存;而不连续显存模型对空洞做了优化处理,不会为空洞分配page结构体。和平坦显存模型相比,不连续显存模型是更好的选择。
稀疏显存模型是实验性的,尽量不要选择稀疏显存模型,除非显存的化学地址空间很稀疏,或则要支持显存热拔插。其他情况应当选择不连续显存模型。
五级结构
显存管理子系统使用节点(node)、区域(zone)和页(page)五级结构描述化学显存。
1.显存节点
显存节点分两种情况。
(1)NUMA系统的显存节点,按照处理器和显存的距离界定。
(2)在具有不连续显存的UMA系统中,表示比区域的级别更高的显存区域,依照化学地址是否连续界定,每块化学地址连续的显存是一个显存节点。
如图所示,显存节点使用一个pglist_data结构体描述显存布局。内核定义了宏NODE_DATA(nid),它拿来获取节点的pglist_data实例。对于平坦显存模型,只有一个pglist_data实例:contig_page_data。
显存节点的pglist_data实例
成员node_id是节点标示符。
成员node_zones是显存区域链表,成员nr_zones是显存节点包含的显存区域的数目。
成员node_start_pfn是起始化学页号,成员node_present_pages是实际存在的化学页的总量,成员node_spanned_pages是包括空洞的化学页总量。
成员node_mem_map指向页描述符字段,每位化学页对应一个页描述符。注意:成员node_mem_map可能不是指向链表的第一个元素,由于页描述符字段的大小必须对齐到2的(MAX_ORDER−1)次方,(MAX_ORDER−1)是页分配器可分配的最大阶数。
pglist_data结构体的主要成员如下:
include/linux/mmzone.h
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 /* 除了稀疏内存模型以外 */
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; /* 物理页总数 */
unsigned long node_spanned_pages; /* 物理页范围的总长度,包括空洞 */
int node_id; /* 节点标识符 */
…
} pg_data_t;
2.显存区域
显存节点被界定为显存区域,内核定义的区域类型如下:
include/linux/mmzone.h
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
};
DMA区域(ZONE_DMA):DMA是“DirectMemoryAccess”的简写,意思是直接显存访问。假如有些设备不能直接访问所有显存,须要使用DMA区域。诸如旧的工业标准体系结构(IndustryStandardArchitecture,ISA)总线只能直接访问16MB以下的显存。
DMA32区域(ZONE_DMA32):64位系统,假如既要支持只能直接访问16MB以下显存的设备,又要支持只能直接访问4GB以下显存的32位设备,这么必须使用DMA32区域。
普通区域(ZONE_NORMAL):直接映射到内核虚拟地址空间的显存区域,译音为“普通区域”,译音为“直接映射区域”或“线性映射区域”。内核虚拟地址和化学地址是线性映射的关系,即虚拟地址=(化学地址+常量)。是否须要使用页表映射?不同处理器的实现不同,比如ARM处理器须要使用页表映射,而MIPS处理器不须要使用页表映射。
高档显存区域(ZONE_HIGHMEM):这是32位时代的产物,内核和用户地址空间按1:3界定,内核地址空间只有1GB,不能把1GB以上的显存直接映射到内核地址空间,把不能直接映射的显存界定到高档显存区域。一般把DMA区域、DMA32区域和普通区域也称为高端显存区域。64位系统的内核虚拟地址空间特别大,不再须要高档显存区域。
可联通区域(ZONE_MOVABLE):它是一个伪显存区域,拿来避免显存碎片,前面讲反碎片技术的时侯具体描述。
设备区域(ZONE_DEVICE):为支持持久显存(persistentmemory)热拔插降低的显存区域。
每位显存区域用一个zone结构体描述,其主要成员如下:
include/linux/mmzone.h
struct zone {
unsigned long watermark[NR_WMARK]; /* 页分配器使用的水线 */
…
long lowmem_reserve[MAX_NR_ZONES]; /* 页分配器使用,当前区域保留多少页不能借给
高的区域类型 */
…
struct pglist_data *zone_pgdat; /* 指向内存节点的pglist_data实例 */
struct per_cpu_pageset __percpu *pageset; /* 每处理器页集合 */
…
unsigned long zone_start_pfn; /* 当前区域的起始物理页号 */
unsigned long managed_pages; /* 伙伴分配器管理的物理页的数量 */
unsigned long spanned_pages; /* 当前区域跨越的总页数,包括空洞 */
unsigned long present_pages; /* 当前区域存在的物理页的数量,不包括空洞 */
const char *name; /* 区域名称 */
…
struct free_area free_area[MAX_ORDER]; /* 不同长度的空闲区域 */
…
}
3.化学页
每位化学页对应一个page结构体,称为页描述符,显存节点的pglist_data实例的成员node_mem_map指向该显存节点包含的所有数学页的页描述符组成的链表。
结构体page的成员flags的布局如下:
| [SECTION] | [NODE] | ZONE | [LAST_CPUPID] | ... | FLAGS |
其中,SECTION是稀疏显存模型中的段编号,NODE是节点编号,ZONE是区域类型,FLAGS是标志位。
内联函数page_to_nid拿来得到化学页所属的显存节点的编号,page_zonenum拿来得到化学页所属的显存区域的类型。
include/linux/mm.h
static inline int page_to_nid(const struct page *page)
{
return (page->flags >> NODES_PGSHIFT) & NODES_MASK;
}
static inline enum zone_type page_zonenum(const struct page *page)
{
return (page->flags >> ZONES_PGSHIFT) & ZONES_MASK;
}
头文件“include/linux/mm_types.h”定义了page结构体。由于数学页的数目很大,所以在page结构体中降低1个成员,可能造成所有page实例占用的显存急剧降低。为了减轻显存消耗,内核努力使page结构体尽可能小,对于不会同时生效的成员,使用联合体,这些做法带来的负面影响是page结构体的可读性差。
原文链接:(侵删)
往期精彩回顾:
最新干货!使用eBPFLSM热修补Linux内核漏洞
深度分析Linux内核通用数组与显存池的使用
盘点这些Linux内核调试手段——内核复印
Linux环境下网路剖析和抓包是如何操作的?
探讨ARM64Linux内核页表的块映射