###NUMA
NUMA的可以从两方面来定义:硬件和Linux软件。
1. 硬件
从硬件的角度来讲,NUMA系统就是一个包含多个部件,且每个
部件包含0个或多个CPU,内存以及IO总线。为了和软件的概念
相区分,这里将每一个部件成为“CELL”。
NUMA系统中的每个“CELL”可以看作一个SMP系统的子集,尽管
某些对于一个独立的SMP系统必需的部件,在这些CELL中都没
有。系统中的各个CELL通过某种系统连接进行互联,例如,交
叉开关或者点到点的连接。这些连接方式可以相互结合,以连
接不同距离的节点。
Linux关心的NUMA系统主要是ccNUMA(缓存一致性NUMA),缓
存的一致性主要是由硬件缓存和系统互联解决的。不同的CELL
中的CPU访问同一个CELL中的内存所用的时间以及有效内存带
宽也是不同的,因此在NUMA系统中,对内存的安排应尽可能的
使用本地(该CELL)的内存,或者最近的内存。
2. Linux 软件
Linux中将硬件资源分割成多个软件抽象的“节点”,每个节点
与物理上的CELL相对应。同样,每个节点中可能会包括0个或
多个CPU,内存以及IO总线。另外,映射到相近的CELL的节点
互相访问对方的内存速度和带宽都相对较快。
在某些平台上,比如x86,Linux就会隐藏那些不包含内存的
节点,而是将这些节点上的CPU附加到其他包含内存的节点上。
因此,我们就不能想当然的认为同一个节点中的CPU对本地内
存的访问用时和带宽都是相同的。
在某些平台上,还是以x86为例,Linux支持模拟NUMA。NUMA
模拟将已存在的节点或者是非NUMA平台的内存进行瓜分,每
一个模拟的节点管理潜在的CELL的部分内存。模拟NUMA的好
处是可以在非NUMA平台上测试支持NUMA的内核以及应用程序。
另外,也可以作为cpuset子系统中一种管理内存的机制。
每一个节点都由自己独立的内存管理子系统,包括正在使用
的页的链表,空闲页的链表,使用统计以及间接访问将会使
用的锁。另外,每个还构建了一个管理区的列表,包括ZONE_DMA,
ZONE_DMA32,ZONE_NORMAL,ZONE_HIGHMEM以及ZONE_MOVABLE。
和非NUMA系统的内存管理一样,当某个节点中一个内存区的
内存分配失败时,这个列表指定了候选的内存区。关于候选
内存区如何进行选择的问题,请参考[2]。
一个节点会尽量满足本地CPU的内存请求,从其所请求的第一
个本地内存的内存区中分配内存。如果本地内存分配不能满
足内存分配的请求时,会从其他的节点内存的合适的内存区
中分配内存。
本地分配会使之后对分配内存的访问相对于物理资源本地化,
且不使用系统连接,只要进程在分配内存之后不会迁移到其
他的节点。Linux调度器对平台的NUMA拓扑非常熟悉,因此
可以最小化进程在节点之间的迁移,然而如果系统不平衡时,
就可能出现迁移的情况。
为了提高NUMA的局部性,系统管理员和应用程序设计者可以
使用绑定CPU的一些命令行接口,例如,taskset,numactl,
以及应用程序接口sched_setaffinity。甚至可以修改内核
的内存分配方式为Linux NUMA内存策略[3]。
对于某些不隐藏无内存的节点的平台来说,这些无内存的节
点就使用离它最近的包含内存的节点中的内存。从这些节点
中分配内存。
当然,某些平台可能不希望在分配内存失败的时候从其他的
内存节点分配内存,而是希望能够得到通知:该节点内存已
经用完。通常这种内存分配方式的典型步骤是:首先使用numa_node_id
或CPU_to_node获取当前CPU所属节点的ID,然后指定从给定
ID的节点中分配内存,如果分配失败的话,这个失败由请求
者自己进行处理。使用这种分配方式的最典型的一个例子就
是slab。有些子系统在内存分配失败的时候会选择禁止该子
系统,例如,内核性能分析子系统。
ref
1. Documentation/vm/numa
2. Documentation/sysctl/vm.txt
3. Documentation/vm/numa_memory_policy.txt