目录
1、静态RAM SRAM
用于CPU高速缓存的L1Cache,L2Cache,L3Cache 访问周期 1-30个时钟周期,容量小,价高。
2、动态RAM DRAM
主存,访问周期 50-200个时钟周期,造价便宜,容量大。
内核以页为单位对物理内存进行管理,每页大小4K ,使用struct page结构体来进行管理
内核为内个物理页定义了一个索引编号PFN(Page Frame Number)
page_to_pfn与pfn_to_page完成PFN与物理页page结构之间的相互转换
node,内核使用struct pglist_data 表示用于管理连续物理内存的node节点
通过 arch_pfn_to_nid 可以根据物理页的PFN定位到物理页所在的Node
通过 page_to_nid 可以根据物理页page定义到page所在node
一致性内存访问 SMP
CPU通过总线访问内存,随着数量增加,总线带宽成为瓶颈
非一致性内存访问 NUMA
CPU访问本地内存更快,不通过总线。访问其他节点的内存,根据距离远近,速度不同。
在NUMA架构下,可控制CPU如何请求内存分配策略
MPOL_BIND | 必须在绑定的节点进行内存分配,如果内存不足,则进行 swap |
MPOL_INTERLEAVE | 本地节点和远程节点均可允许分配内存 |
MPOL_PREFERRED | 优先在指定节点分配内存,当指定节点内存不足时,选择离指定节点最近的节点分配内存 |
MPOL_LOCAL (默认) | 优先在本地节点分配,当本地节点内存不足时,可以在远程节点分配内存 |
API接口
- #include
-
- long set_mempolicy(int mode, const unsigned long *nodemask,
- unsigned long maxnode);
numactl -H 查看NUMA的配置
numactl -s查看NUMA的内存分配策略
numastat
查看各个NUMA节点的内存访问命中率
numactl 命令查看 ,指定应用程序运行在哪些CPU核心上,同时也可以绑定应用程序可以在哪些NUMA节点上分配内存。
numactl --membind=nodes --cpunodebind=nodes command
-membind 指定应用程序只能在哪些NUMA节点上分配内存,如果这些节点内存不足,则分配失败;
-cpunodebind 执行应用程序只能在哪些NUMA上运行
-physcpubind
将我们的应用程序绑定到具体的物理 CPU 上
将进程 a.out绑定到 0~15 CPU 上执行
numactl --physcpubind= 0-15 ./a.out
struct pglist_data 的全局数组node_data[] 来管理所有NUMA节点。
源码位置(arch/arm64/include/asm/mmzone.h)
- #define CONFIG_NUMA
- extern struct pglist_data *node_data[];
- #define NODE_DATA(nid) (node_data[(nid)])
NODE_DATA(nid) 宏可以通过NUMA节点的node id 找到对应的struct pglist_data结构
- typedef struct pglist_data {
- // NUMA 节点id
- int node_id;
- // 指向 NUMA 节点内管理所有物理页 page 的数组
- struct page *node_mem_map;
- // NUMA 节点内第一个物理页的 pfn
- unsigned long node_start_pfn;
- // NUMA 节点内所有可用的物理页个数(不包含内存空洞)
- unsigned long node_present_pages;
- // NUMA 节点内所有的物理页个数(包含内存空洞)
- unsigned long node_spanned_pages;
- // 保证多进程可以并发安全的访问 NUMA 节点
- spinlock_t node_size_lock;
-
- }
物理内存区域的功能不同,物理内存划分为
- struct zone {
- // 防止并发访问该内存区域
- spinlock_t lock;
- // 内存区域名称:Normal ,DMA,HighMem
- const char *name;
- // 指向该内存区域所属的 NUMA 节点
- struct pglist_data *zone_pgdat;
- // 属于该内存区域中的第一个物理页 PFN
- unsigned long zone_start_pfn;
- // 该内存区域中所有的物理页个数(包含内存空洞)
- unsigned long spanned_pages;
- // 该内存区域所有可用的物理页个数(不包含内存空洞)
- unsigned long present_pages;
- // 被伙伴系统所管理的物理页数
- atomic_long_t managed_pages;
- // 伙伴系统的核心数据结构
- struct free_area free_area[MAX_ORDER];
- // 该内存区域内存使用的统计信息
- atomic_long_t vm_stat[NR_VM_ZONE_STAT_ITEMS];
- } ____cacheline_internodealigned_in_smp;
伙伴系统有个特点,所分配的物理内存页都是物理上连续的,并且只能分配2的整数幂个页(阶)
node ->zone->page
page(页)是linux内核管理物理内存的最小单位
alloc_page
alloc_pagesget_free_pages
get_zeroed_pagefree_page
分配的节点 gfp
#define ___GFP_DMA 0x01u
#define ___GFP_HIGHMEM 0x02u
#define ___GFP_DMA32 0x04u
#define ___GFP_MOVABLE 0x08u
如果最高级内存区域不足时, 按照ZONE_HIGHMEM->ZONE_NORMAL->ZONE_DMA 依次降级申请。
适用于中断程序,以及持有自旋锁的进程上下文中。
适用于安全调度的进程上下文中。
- #define ALLOC_WMARK_MIN WMARK_MIN
- #define ALLOC_WMARK_LOW WMARK_LOW
- #define ALLOC_WMARK_HIGH WMARK_HIGH
- #define ALLOC_NO_WATERMARKS 0x04 /* don't check watermarks at all */
-
- #define ALLOC_HARDER 0x10 /* try to alloc harder */
- #define ALLOC_HIGH 0x20 /* __GFP_HIGH set */
- #define ALLOC_CPUSET 0x40 /* check for correct cpuset */
-
- #define ALLOC_KSWAPD 0x800 /* allow waking of kswapd, __GFP_KSWAPD_RECLAIM set */
WMARK_MIN(页最小阈值), WMARK_LOW (页低阈值),WMARK_HIGH(页高阈值)。
文件页:物理内存页中的数据来自磁盘文件,当对文件读取时候,内核会根据局部性原理将读取磁盘数据缓存在page cache中,cacha page就是存放的文件页。再次读取时,从cache中读。
回收时,直接回收即可,再次读取时,从磁盘重新读取。但,文件页修改还没来得及回写到磁盘,此时的文件页是脏页,不能进行回收,需要将脏页回写到磁盘才能回收。
匿名页:背后没有一个磁盘作为数据源,数据时通过进程运行过程中产生,比如动态分配的堆内存。
回收时,都需要将匿名页中的数据先保存在磁盘中,然后再回收。当进程再次访问这块内存时,重新把数据从磁盘空间读到内存,这块磁盘空间是可以单独是swap分区或者是一个特殊的swap文件。匿名页的回收机制就是常看到的swap机制。
内核回收的是文件页还是匿名页
cat /proc/sys/vm/swappiness
命令查看,
swappiness 选项的取值范围为 0 到 100,默认为 60。
swappiness 用于表示 Swap 机制的积极程度,
数值越大,Swap 的积极程度越高,内核越倾向于回收匿名页。
数值越小,Swap 的积极程度越低。内核就越倾向于回收文件页。
快速路径分配
get_page_from_freelist
慢速路径分配
__alloc_pages_slowpath
慢速路径初始化参数
retry_cpuset:
调整内存分配策略alloc_flags,采用更加激进方式
内存分配主要在允许的CPU相关联的NUMA节点上
内存水位线下调至WMARK_LOW
唤醒所有kswapd进程进行异步内存回收
触发直接内存整理direct_compact获取更多内存
retry:
进一步调整内存分配aloc_flags,使用更加激进的内存分配手段
在内存分配时忽略水位线
直接触发内存回收direct_reclaim
再次触发直接内存整理direct_compact
OOM机制
nopage:
以上仍然不能分配,如果设置__GFP_NOFAIL不允许失败,则不停重试以上分配过程
fail:
分配失败,输出经过信息。
got_pg
内存分配成功,返回新申请的内存块
return page;
direct_compact:
在页面回收时,把可移动的聚在一起,不可以移动的聚在一起,去碎片化,然后进行成块回收。
学习链接:
参考
https://course.0voice.com/v1/course/intro?courseId=2&agentId=0