云计算该怎么学—Linux内存管理!
一、物理内存的组织形式:
由于物理内存是连续的,页也是连续的,每个页的大小一样,从0开始给每个页编号,每个页用struct page表示,存放在一个大数组里。因此对于任何一个地址,只要除以页的大小,就可以得到对应页的编号,根据下标就可以找到对应的struct page结构,这种模型是最经典的平坦内存模型:
所有的CPU总是通过总线去访问内存,这是最经典的内存使用方法,它可以使用平坦内存模型来管理内存:
在这种模式下,所有的CPU都在总线的一侧,所有的内存组成一大块内存在总线的另外一侧,CPU访问内存都需要通过总线访问,而且距离都是一样的,这种模式称为SMP(Symmetric multiprocessing),即为对称多处理器。这种模式有一个显著的缺点,就是每个CPU访问内存都需要通过总线,那么总线就会成为瓶颈:
为了提高性能,有了一种更加高级的模式,NUMA(Non-uniform memory access),非一致内存访问。这种模式下,内存不是组成连续的一大块,而是每个CPU都有自己的一块内存,CPU访问内存不需要经过总线,所以速度上会更快,每个CPU和内存组成一个NUMA节点。但是在本地内存不足的情况下,每个CPU会去其他NUMA节点申请内存,此时内存的访问时间就比较长
这样内存被分为多个节点,每个节点都分成一个一个的页。由于页是全局唯一定位的,所以每个页都需要有一个全局唯一的页号。但是由于物理内存不再是连续的,所以页号也不是连续的,于是内存模型就变成了非连续内存模型,管理起来就会比较复杂。
二、节点:
下面解析当前主流场景,NUMA方式。
为了表示一个NUMA节点,内核定义了struct pglist_struct这样一个结构体,如下:
typedef struct pglist_data {
struct zone node_zones[MAX_NR_ZONES];
struct zonelist node_zonelists[MAX_ZONELISTS];
int nr_zones;
struct page *node_mem_map;
unsigned long node_start_pfn;
unsigned long node_present_pages;
unsigned long node_spanned_pages;
int node_id;
......
} pg_data_t;
-
每个节点都有自己的ID:node_id
-
node_mem_map是这个节点struct page数组,用于描述这个节点所有的页
-
node_start_pfn是这个节点的起始页号
-
node_spanned_pages是整个物理内存包含的页数目(包括空洞)
-
node_present_pages是真正可用的物理页数目
例如:64M物理内存隔着4M的空洞,然后再是另外的64M,换算成页数目,分别是16K、1K、16K。那么node_spanned_pages就是33K,node_spanned_pages就是32K
每个节点被分为一个一个的区域zone,存放在node_zones数组中,数组的大小为MAX_NR_ZONES,定义如下:
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,
__MAX_NR_ZONES
};
这里说明以下,这些分区都是对物理内存的说明:
- ZONE_DMA:用作DMA的物理内存
- ZONE_DMA32:对于64位CPU,还有这个DMA区域
- ZONE_NORMAL:就是直接映射区
- ZONE_MOVABLE:可移动区域,通过将物理内存划分为可移动分配区域和不可移动分配区域来避免内存碎片
- __MAX_NR_ZONES:内存区域的数量
内核中有一个数组用来存放节点:
struct pglist_data *node_data[MAX_NUMNODES] __read_mostly;
三、区域:
到这里,将内存分为节点,将节点分为区域,下面来看一看区域的定义
区域是zone结构体表示:
struct zone {
......
struct pglist_data *zone_pgdat;
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];
unsigned long flags;
spinlock_t lock;
......
} ____cacheline_internodealigned_in_
- zone_start_pfn:表示这个属于这个zone的第一个页
- spanned_pages:注释里有spanned_pages = zone_end_pfn - zone_start_pfn,表示spanned_pages就是结束页面减去起始页面的页面数,不管中间是否存在空洞
- present_pages:注释里有spanned_pages - absent_pages(pages in holes),表示减去空洞后的页面数
- managed_pages:注释中有managed_pages = present_pages - reserved_pages,表示这个zone中被伙伴系统管理的所有的page数目
- per_cpu_pageset:用于区分冷热页。什么是冷热页?指的是一个页是否被加载进CPU的高速缓存中
四、页:
在了解区域后,再来看组成物理内存最基本的单位页,页的数组结构使用struct page表示。这个结构体定义非常的复杂,因为支持多种使用模式,所以定义了许多union:
struct page {
unsigned long flags;
union {
struct address_space *mapping;
void *s_mem;
atomic_t compound_mapcount;
};
union {
pgoff_t index;
void *freelist;
};
union {
unsigned counters;
struct {
union {
atomic_t _mapcount;
unsigned int active;
struct {
unsigned inuse:16;
unsigned objects:15;
unsigned frozen:1;
};
int units;
};
atomic_t _refcount;
};
};
union {
struct list_head lru;
struct dev_pagemap *pgmap;

加载中…