From b96d9712a84ad54bfbe14df7249e0271e1e57017 Mon Sep 17 00:00:00 2001 From: gatieme Date: Wed, 3 Aug 2016 12:20:10 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84=E4=BA=86README.md...?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../02-memory/01-description/01-memory/1.c | 80 ++++ .../01-description/01-memory/README.md | 441 +++++++----------- 2 files changed, 238 insertions(+), 283 deletions(-) create mode 100644 study/kernel/02-memory/01-description/01-memory/1.c diff --git a/study/kernel/02-memory/01-description/01-memory/1.c b/study/kernel/02-memory/01-description/01-memory/1.c new file mode 100644 index 0000000..5c7425f --- /dev/null +++ b/study/kernel/02-memory/01-description/01-memory/1.c @@ -0,0 +1,80 @@ +/* + * The pg_data_t structure is used in machines with CONFIG_DISCONTIGMEM + * (mostly NUMA machines?) to denote a higher-level memory zone than the + * zone denotes. + * + * On NUMA machines, each NUMA node would have a pg_data_t to describe + * it's memory layout. + * + * Memory statistics and page replacement data structures are maintained on a + * per-zone basis. + */ +struct bootmem_data; +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 /* means !SPARSEMEM */ + struct page *node_mem_map; +#ifdef CONFIG_PAGE_EXTENSION + struct page_ext *node_page_ext; +#endif +#endif +#ifndef CONFIG_NO_BOOTMEM + struct bootmem_data *bdata; +#endif +#ifdef CONFIG_MEMORY_HOTPLUG + /* + * Must be held any time you expect node_start_pfn, node_present_pages + * or node_spanned_pages stay constant. Holding this will also + * guarantee that any pfn_valid() stays that way. + * + * pgdat_resize_lock() and pgdat_resize_unlock() are provided to + * manipulate node_size_lock without checking for CONFIG_MEMORY_HOTPLUG. + * + * Nests above zone->lock and zone->span_seqlock + */ + spinlock_t node_size_lock; +#endif + unsigned long node_start_pfn; + unsigned long node_present_pages; /* total number of physical pages */ + unsigned long node_spanned_pages; /* total size of physical page + range, including holes */ + int node_id; + wait_queue_head_t kswapd_wait; + wait_queue_head_t pfmemalloc_wait; + struct task_struct *kswapd; /* Protected by + mem_hotplug_begin/end() */ + int kswapd_max_order; + enum zone_type classzone_idx; +#ifdef CONFIG_COMPACTION + int kcompactd_max_order; + enum zone_type kcompactd_classzone_idx; + wait_queue_head_t kcompactd_wait; + struct task_struct *kcompactd; +#endif +#ifdef CONFIG_NUMA_BALANCING + /* Lock serializing the migrate rate limiting window */ + spinlock_t numabalancing_migrate_lock; + + /* Rate limiting time interval */ + unsigned long numabalancing_migrate_next_window; + + /* Number of pages migrated during the rate limiting time interval */ + unsigned long numabalancing_migrate_nr_pages; +#endif + +#ifdef CONFIG_DEFERRED_STRUCT_PAGE_INIT + /* + * If memory initialisation on large machines is deferred then this + * is the first PFN that needs to be initialised. + */ + unsigned long first_deferred_pfn; +#endif /* CONFIG_DEFERRED_STRUCT_PAGE_INIT */ + +#ifdef CONFIG_TRANSPARENT_HUGEPAGE + spinlock_t split_queue_lock; + struct list_head split_queue; + unsigned long split_queue_len; +#endif +} pg_data_t; \ No newline at end of file diff --git a/study/kernel/02-memory/01-description/01-memory/README.md b/study/kernel/02-memory/01-description/01-memory/README.md index 1d1870c..5d71475 100644 --- a/study/kernel/02-memory/01-description/01-memory/README.md +++ b/study/kernel/02-memory/01-description/01-memory/README.md @@ -11,6 +11,9 @@ 前面我们讲到[服务器体系(SMP, NUMA, MPP)与共享存储器架构(UMA和NUMA)](http://blog.csdn.net/gatieme/article/details/52098615) +#1.1 UMA和NUMA两种模型 +------- + 共享存储型多处理机有两种模型 * 均匀存储器存取(Uniform-Memory-Access,简称UMA)模型 @@ -18,8 +21,8 @@ 将可用内存以连续方式组织起来, * 非均匀存储器存取(Nonuniform-Memory-Access,简称NUMA)模型 - -**UMA** +##1.2 UMA模型 +------- 传统的多核运算是使用SMP(Symmetric Multi-Processor )模式:将多个处理器与一个集中的存储器和I/O总线相连。所有处理器只能访问同一个物理存储器,因此SMP系统有时也被称为一致存储器访问(UMA)结构体系,一致性意指无论在什么时候,处理器只能为内存的每个数据保持或共享唯一一个数值。 @@ -29,7 +32,8 @@ 很显然,SMP的缺点是可伸缩性有限,因为在存储器和I/O接口达到饱和的时候,增加处理器并不能获得更高的性能,与之相对应的有AMP架构,不同核之间有主从关系,如一个核控制另外一个核的业务,可以理解为多核系统中控制平面和数据平面。 -**NUMA** +##1.3 NUMA模型 +------- NUMA模式是一种分布式存储器访问方式,处理器可以同时访问不同的存储器地址,大幅度提高并行性。 NUMA总是多处理器计算机,系统的哪个CPU都有本地内存, 可支持快速的访问, 各个处理器之前通过总线链接起来, 以支持堆其他CPU的本地内存的访问, 当然访问要比本地内存慢. @@ -42,13 +46,21 @@ NUMA模式下,处理器被划分成多个"节点"(node), 每个节点被 NUMA 的主要优点是伸缩性。NUMA 体系结构在设计上已超越了 SMP 体系结构在伸缩性上的限制。通过 SMP,所有的内存访问都传递到相同的共享内存总线。这种方式非常适用于 CPU 数量相对较少的情况,但不适用于具有几十个甚至几百个 CPU 的情况,因为这些 CPU 会相互竞争对共享内存总线的访问。NUMA 通过限制任何一条内存总线上的 CPU 数量并依靠高速互连来连接各个节点,从而缓解了这些瓶颈状况。 -#(N)UMA模型中linux内存的机构 + +#2 (N)UMA模型中linux内存的机构 ------- + Linux适用于各种不同的体系结构, 而不同体系结构在内存管理方面的差别很大. 因此linux内核需要用一种体系结构无关的方式来表示内存. Linux内核通过插入一些兼容层, 使得不同体系结构的差异很好的被隐藏起来, 内核对一致和非一致内存访问使用相同的数据结构 + +#2.1 (N)UMA模型中linux内存的机构 +------- + + + 非一致存储器访问(NUMA)模式下 * 处理器被划分成多个"节点"(node), 每个节点被分配有的本地存储器空间. 所有节点中的处理器都可以访问全部的系统物理存储器,但是访问本节点内的存储器所需要的时间,比访问某些远程节点内的存储器所花的时间要少得多 @@ -60,22 +72,23 @@ Linux内核通过插入一些兼容层, 使得不同体系结构的差异很好 -#物理内存描述 +#2.2 Linux物理内存的组织形式 ------- Linux把物理内存划分为三个层次来管理 -* 存储节点(Node) - -* 管理区(Zone) - -* 页面(Page) +| 层次 | 描述 | +|:----:|:----:| +| 存储节点(Node) | CPU被划分为多个节点(node), 内存则被分簇, 每个CPU对应一个本地物理内存, 即一个CPU-node对应一个内存簇bank,即每个内存簇被认为是一个节点 | +| 管理区(Zone) | 每个物理内存节点node被划分为多个内存管理区域, 用于表示不同范围的内存, 内核可以使用不同的映射方式映射物理内存 | +| 页面(Page) | 内存被细分为多个页面帧, 页面是最基本的页面分配的单位 | 为了支持NUMA模型,也即CPU对不同内存单元的访问时间可能不同,此时系统的物理内存被划分为几个节点(node), 一个node对应一个内存簇bank,即每个内存簇被认为是一个节点 -* 首先, 内存被划分为结点. 每个节点关联到系统中的一个处理器, 内核中表示为pg_data_t的实例. +* 首先, 内存被划分为结点. 每个节点关联到系统中的一个处理器, 内核中表示为`pg_data_t`的实例. 系统中每个节点被链接到一个以NULL结尾的`pgdat_list`链表中<而其中的每个节点利用`pg_data_tnode_next`字段链接到下一节.而对于PC这种UMA结构的机器来说, 只使用了一个成为contig_page_data的静态pg_data_t结构. + +* 接着各个节点又被划分为内存管理区域, 一个管理区域通过struct zone_struct描述, 其被定义为zone_t, 用以表示内存的某个范围, 低端范围的16MB被描述为ZONE_DMA, 某些工业标准体系结构中的(ISA)设备需要用到它, 然后是可直接映射到内核的普通内存域ZONE_NORMAL,最后是超出了内核段的物理地址域ZONE_HIGHMEM, 被称为高端内存. 是系统中预留的可用内存空间, 不能被内核直接映射. -* 接着各个节点又被划分为内存域, 低端的16MB被划分为ZONE_DMA, 然后是可直接映射到内核的普通内存域ZONE_NORMAL,最后是超出了内核段的物理地址域ZONE_HIGHMEM, 被称为高端内存. 在一个单独的节点内,任一给定CPU访问页面所需的时间都是相同的。然而,对不同的CPU,这个时间可能就不同。对每个CPU而言,内核都试图把耗时节点的访问次数减到最少这就要小心地选择CPU最常引用的内核数据结构的存放位置. @@ -84,17 +97,29 @@ Linux把物理内存划分为三个层次来管理 在LINUX中引入一个数据结构`struct pglist_data` ,来描述一个node,定义在[`include/linux/mmzone.h`](http://lxr.free-electrons.com/source/include/linux/mmzone.h#L630) 文件中。(这个结构被typedef pg_data_t)。 -* 对于NUMA系统来讲, 整个系统的内存由一个[node_data](http://lxr.free-electrons.com/source/arch/s390/numa/numa.c#L23)的pg_data_t指针数组来管理。(因为可能有多个node),系统中的每个节点链接到一个以NULL结尾的[pgdat_list](http://lxr.free-electrons.com/source/arch/ia64/include/asm/numa.h#L27)链表中,而其中的每个节点利用pd_data_tnode_next字段链接到下一个节点。 +* 对于NUMA系统来讲, 整个系统的内存由一个[node_data](http://lxr.free-electrons.com/source/arch/s390/numa/numa.c?v=4.7#L23)的pg_data_t指针数组来管理。(因为可能有多个node),系统中的每个节点链接到一个以NULL结尾的[pgdat_list](http://lxr.free-electrons.com/source/arch/ia64/include/asm/numa.h#L27)链表中,而其中的每个节点利用pd_data_tnode_next字段链接到下一个节点。 * 对于PC这样的UMA系统,使用struct pglist_datacontig_page_data ,作为系统唯一的node管理所有的内存区域。(UMA系统中中只有一个node) -#内存区域的划分 + + +##内存节点node ------- -##内存节点node与zone管理区 -------- +>CPU被划分为多个节点(node), 内存则被分簇, 每个CPU对应一个本地物理内存, 即一个CPU-node对应一个内存簇bank,即每个内存簇被认为是一个节点 +> +>系统的物理内存被划分为几个节点(node), 一个node对应一个内存簇bank,即每个内存簇被认为是一个节点 + +* 首先, 内存被划分为结点. 每个节点关联到系统中的一个处理器, 内核中表示为`pg_data_t`的实例. 系统中每个节点被链接到一个以NULL结尾的`pgdat_list`链表中<而其中的每个节点利用`pg_data_tnode_next`字段链接到下一节.而对于PC这种UMA结构的机器来说, 只使用了一个成为contig_page_data的静态pg_data_t结构. + + +内存中的每个节点都是由pg_data_t描述,而pg_data_t由struct pglist_data定义而来, 该数据结构定义在[include/linux/mmzone.h, line 615](http://lxr.free-electrons.com/source/include/linux/mmzone.h#L615) + + +在分配一个页面时, Linux采用节点局部分配的策略, 从最靠近运行中的CPU的节点分配内存, 由于进程往往是在同一个CPU上运行, 因此从当前节点得到的内存很可能被用到 + 在内存中,每个簇所对应的node又被分成的称为管理区(zone)的块,它们各自描述在内存中的范围。一个管理区(zone)由[struct zone](http://lxr.free-electrons.com/source/include/linux/mmzone.h#L326)结构体来描述,在linux-2.4.37之前的内核中是用[`typedef struct zone_struct zone_t `](http://lxr.free-electrons.com/source/include/linux/mmzone.h?v=2.4.37#L47)数据结构来描述) @@ -106,6 +131,7 @@ Linux把物理内存划分为三个层次来管理 不同的管理区的用途是不一样的,ZONE_DMA类型的内存区域在物理内存的低端,主要是ISA设备只能用低端的地址做DMA操作。ZONE_NORMAL类型的内存区域直接被内核映射到线性地址空间上面的区域(line address space),ZONE_HIGHMEM将保留给系统使用,是系统中预留的可用内存空间,不能被内核直接映射。 对于x86机器,管理区(内存区域)类型如下分布 + | 类型 | 区域 | | :------- | ----: | | ZONE_DMA | 0~16MB | @@ -143,63 +169,90 @@ x86体系结构中,page的大小为4096个字节。 >[Linux-2.6.32 NUMA架构之内存和调度](http://www.cnblogs.com/zhenjing/archive/2012/03/21/linux_numa.html) -表示node的数据结构为[`typedef struct pglist_data pg_data_t`](http://lxr.free-electrons.com/source/include/linux/mmzone.h#L630), 这个结构定义在[linux/mmzone.h](http://lxr.free-electrons.com/source/include/linux/mmzone.h#L691)中,结构体的内容如下 +表示node的数据结构为[`typedef struct pglist_data pg_data_t`](http://lxr.free-electrons.com/source/include/linux/mmzone.h#L630), 这个结构定义在[include/linux/mmzone.h, line 615](http://lxr.free-electrons.com/source/include/linux/mmzone.h#L615)中,结构体的内容如下 - -```cpp +```c +```c +/* + * The pg_data_t structure is used in machines with CONFIG_DISCONTIGMEM + * (mostly NUMA machines?) to denote a higher-level memory zone than the + * zone denotes. + * + * On NUMA machines, each NUMA node would have a pg_data_t to describe + * it's memory layout. + * + * Memory statistics and page replacement data structures are maintained on a + * per-zone basis. + */ +struct bootmem_data; 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 /* means !SPARSEMEM */ - struct page *node_mem_map; -#ifdef CONFIG_MEMCG - struct page_cgroup *node_page_cgroup; + struct zone node_zones[MAX_NR_ZONES]; + struct zonelist node_zonelists[MAX_ZONELISTS]; + int nr_zones; +#ifdef CONFIG_FLAT_NODE_MEM_MAP /* means !SPARSEMEM */ + struct page *node_mem_map; +#ifdef CONFIG_PAGE_EXTENSION + struct page_ext *node_page_ext; #endif #endif #ifndef CONFIG_NO_BOOTMEM - struct bootmem_data *bdata; + struct bootmem_data *bdata; #endif #ifdef CONFIG_MEMORY_HOTPLUG - /* - * Must be held any time you expect node_start_pfn, node_present_pages - * or node_spanned_pages stay constant. Holding this will also - * guarantee that any pfn_valid() stays that way. - * - * Nests above zone->lock and zone->size_seqlock. - */ - spinlock_t node_size_lock; + /* + * Must be held any time you expect node_start_pfn, node_present_pages + * or node_spanned_pages stay constant. Holding this will also + * guarantee that any pfn_valid() stays that way. + * + * pgdat_resize_lock() and pgdat_resize_unlock() are provided to + * manipulate node_size_lock without checking for CONFIG_MEMORY_HOTPLUG. + * + * Nests above zone->lock and zone->span_seqlock + */ + spinlock_t node_size_lock; +#endif + unsigned long node_start_pfn; + unsigned long node_present_pages; /* total number of physical pages */ + unsigned long node_spanned_pages; /* total size of physical page + range, including holes */ + int node_id; + wait_queue_head_t kswapd_wait; + wait_queue_head_t pfmemalloc_wait; + struct task_struct *kswapd; /* Protected by + mem_hotplug_begin/end() */ + int kswapd_max_order; + enum zone_type classzone_idx; +#ifdef CONFIG_COMPACTION + int kcompactd_max_order; + enum zone_type kcompactd_classzone_idx; + wait_queue_head_t kcompactd_wait; + struct task_struct *kcompactd; #endif - unsigned long node_start_pfn; - unsigned long node_present_pages; /* total number of physical pages */ - unsigned long node_spanned_pages; /* total size of physical page - range, including holes */ - int node_id; - nodemask_t reclaim_nodes; /* Nodes allowed to reclaim from */ - wait_queue_head_t kswapd_wait; - wait_queue_head_t pfmemalloc_wait; - struct task_struct *kswapd; /* Protected by lock_memory_hotplug() */ - int kswapd_max_order; - enum zone_type classzone_idx; #ifdef CONFIG_NUMA_BALANCING - /* Lock serializing the migrate rate limiting window */ - spinlock_t numabalancing_migrate_lock; + /* Lock serializing the migrate rate limiting window */ + spinlock_t numabalancing_migrate_lock; - /* Rate limiting time interval */ - unsigned long numabalancing_migrate_next_window; + /* Rate limiting time interval */ + unsigned long numabalancing_migrate_next_window; - /* Number of pages migrated during the rate limiting time interval */ - unsigned long numabalancing_migrate_nr_pages; + /* Number of pages migrated during the rate limiting time interval */ + unsigned long numabalancing_migrate_nr_pages; #endif - /* reserved for Red Hat */ - RH_KABI_RESERVE(1) - RH_KABI_RESERVE(2) - RH_KABI_RESERVE(3) - RH_KABI_RESERVE(4) +#ifdef CONFIG_DEFERRED_STRUCT_PAGE_INIT + /* + * If memory initialisation on large machines is deferred then this + * is the first PFN that needs to be initialised. + */ + unsigned long first_deferred_pfn; +#endif /* CONFIG_DEFERRED_STRUCT_PAGE_INIT */ +#ifdef CONFIG_TRANSPARENT_HUGEPAGE + spinlock_t split_queue_lock; + struct list_head split_queue; + unsigned long split_queue_len; +#endif } pg_data_t; - ``` | 字段| 描述 | @@ -218,7 +271,7 @@ typedef struct pglist_data { | classzone_idx | 这个字段暂时没弄明白,不过其中的zone_type是对ZONE_DMA,ZONE_DMA32,ZONE_NORMAL,ZONE_HIGH,ZONE_MOVABLE,__MAX_NR_ZONES的枚举 | -在新的linux3.x~linux4.x的内核中,Linux定义了一个大小为[MAX_NUMNODES](http://lxr.free-electrons.com/source/include/linux/numa.h#L11)类型为[`pgdat_list`](http://lxr.free-electrons.com/source/arch/ia64/mm/discontig.c#L50)数组,数组的大小根据[CONFIG_NODES_SHIFT](http://lxr.free-electrons.com/source/include/linux/numa.h#L6)的配置决定。对于UMA来说,NODES_SHIFT为0,所以MAX_NUMNODES的值为1。内核提供了[for_each_online_pgdat(pgdat)](http://lxr.free-electrons.com/source/include/linux/mmzone.h#L867) 来遍历节点 +在新的linux3.x~linux4.x的内核中,Linux定义了一个大小为[MAX_NUMNODES](http://lxr.free-electrons.com/source/include/linux/numa.h#L11)类型为[`pgdat_list`](http://lxr.free-electrons.com/source/arch/ia64/mm/discontig.c#L50)数组,数组的大小根据[CONFIG_NODES_SHIFT](http://lxr.free-electrons.com/source/include/linux/numa.h#L6)的配置决定。对于UMA来说,NODES_SHIFT为0,所以MAX_NUMNODES的值为1。内核提供了[for_each_online_pgdat(pgdat)](http://lxr.free-electrons.com/source/include/linux/mmzone.h?v=4.7#L871)来遍历节点 而在linux-2.4.x之前的内核中所有的节点,都由一个被称为[pgdat_list](http://lxr.free-electrons.com/source/include/linux/mmzone.h?v=2.4.37#L169)的链表维护。这些节点都放在该链表中,均由函数[init_bootmem_core()](http://lxr.free-electrons.com/source/mm/bootmem.c#L96)初始化结点。内核提供了[宏for_each_pgdat(pgdat)]http://lxr.free-electrons.com/source/include/linux/mmzone.h?v=2.4.37#L169)来遍历节点链表。 @@ -226,244 +279,57 @@ typedef struct pglist_data { #管理区Zone ------- + + linux系统中,内存中的每个簇所对应的node又被分成的称为管理区(zone)的块, ->一个管理区(zone)由[struct zone](http://lxr.free-electrons.com/source/include/linux/mmzone.h#L326)结构体来描述(linux-3.8~目前linux4.5),而在linux-2.4.37之前的内核中是用[`typedef struct zone_struct zone_t `](http://lxr.free-electrons.com/source/include/linux/mmzone.h?v=2.4.37#L47)数据结构来描述) +>一个管理区(zone)由[struct zone](http://lxr.free-electrons.com/source/include/linux/mmzone.h#L326)结构体来描述(linux-3.8~目前linux4.5),而在linux-2.4.37之前的内核中是用[`typedef struct zone_struct zone_t `](http://lxr.free-electrons.com/source/include/linux/mmzone.h?v=2.4.37#L47)数据结构来描述) zone对象用于跟踪诸如页面使用情况的统计数,空闲区域信息和锁信息 >里面保存着内存使用状态信息,如page使用统计,未使用的内存区域,互斥访问的锁(LOCKS)等。 -`struct zone`在`linux/mmzone.h`中定义,在linux-4.5的内核中可以使用[`sed -n '326,526p' mmzone.h`](http://blog.csdn.net/gatieme/article/details/51150115)来查看其定义 +`struct zone`在`linux/mmzone.h`中定义,在linux-4.7的内核中可以使用[include/linux/mmzone.h](http://lxr.free-electrons.com/source/include/linux/mmzone.h?v=4.7#L324)来查看其定义 ```cpp -struct zone { - /* Fields commonly accessed by the page allocator */ - - /* zone watermarks, access with *_wmark_pages(zone) macros */ - unsigned long watermark[NR_WMARK]; - - /* - * When free pages are below this point, additional steps are taken - * when reading the number of free pages to avoid per-cpu counter - * drift allowing watermarks to be breached - */ - unsigned long percpu_drift_mark; - - /* - * We don't know if the memory that we're going to allocate will be freeable - * or/and it will be released eventually, so to avoid totally wasting several - * GB of ram we must reserve some of the lower zone memory (otherwise we risk - * to run OOM on the lower zones despite there's tons of freeable ram - * on the higher zones). This array is recalculated at runtime if the - * sysctl_lowmem_reserve_ratio sysctl changes. - */ - unsigned long lowmem_reserve[MAX_NR_ZONES]; - - /* - * This is a per-zone reserve of pages that should not be - * considered dirtyable memory. - */ - unsigned long dirty_balance_reserve; - -#ifdef CONFIG_NUMA - int node; - /* - * zone reclaim becomes active if more unmapped pages exist. - */ - unsigned long min_unmapped_pages; - unsigned long min_slab_pages; -#endif - struct per_cpu_pageset __percpu *pageset; - /* - * free areas of different sizes - */ - spinlock_t lock; - int all_unreclaimable; /* All pages pinned */ -#if defined CONFIG_COMPACTION || defined CONFIG_CMA - /* Set to true when the PG_migrate_skip bits should be cleared */ - bool compact_blockskip_flush; - - /* pfns where compaction scanners should start */ - unsigned long compact_cached_free_pfn; - unsigned long compact_cached_migrate_pfn; -#endif -#ifdef CONFIG_MEMORY_HOTPLUG - /* see spanned/present_pages for more description */ - seqlock_t span_seqlock; -#endif - struct free_area free_area[MAX_ORDER]; - -#ifndef CONFIG_SPARSEMEM - /* - * Flags for a pageblock_nr_pages block. See pageblock-flags.h. - * In SPARSEMEM, this map is stored in struct mem_section - */ - unsigned long *pageblock_flags; -#endif /* CONFIG_SPARSEMEM */ - -#ifdef CONFIG_COMPACTION - /* - * On compaction failure, 1<> PAGE_SHIFT */ - unsigned long zone_start_pfn; - - /* - * spanned_pages is the total pages spanned by the zone, including - * holes, which is calculated as: - * spanned_pages = zone_end_pfn - zone_start_pfn; - * - * present_pages is physical pages existing within the zone, which - * is calculated as: - * present_pages = spanned_pages - absent_pages(pages in holes); - * - * managed_pages is present pages managed by the buddy system, which - * is calculated as (reserved_pages includes pages allocated by the - * bootmem allocator): - * managed_pages = present_pages - reserved_pages; - * - * So present_pages may be used by memory hotplug or memory power - * management logic to figure out unmanaged pages by checking - * (present_pages - managed_pages). And managed_pages should be used - * by page allocator and vm scanner to calculate all kinds of watermarks - * and thresholds. - * - * Locking rules: - * - * zone_start_pfn and spanned_pages are protected by span_seqlock. - * It is a seqlock because it has to be read outside of zone->lock, - * and it is done in the main allocator path. But, it is written - * quite infrequently. - * - * The span_seq lock is declared along with zone->lock because it is - * frequently read in proximity to zone->lock. It's good to - * give them a chance of being in the same cacheline. - * - * Write access to present_pages at runtime should be protected by - * lock_memory_hotplug()/unlock_memory_hotplug(). Any reader who can't - * tolerant drift of present_pages should hold memory hotplug lock to - * get a stable value. - * - * Read access to managed_pages should be safe because it's unsigned - * long. Write access to zone->managed_pages and totalram_pages are - * protected by managed_page_count_lock at runtime. Idealy only - * adjust_managed_page_count() should be used instead of directly - * touching zone->managed_pages and totalram_pages. - */ - unsigned long spanned_pages; - unsigned long present_pages; - unsigned long managed_pages; - - /* - * Number of MIGRATE_RESEVE page block. To maintain for just - * optimization. Protected by zone->lock. - */ - int nr_migrate_reserve_block; - - /* - * rarely used fields: - */ - const char *name; - - - -} ____cacheline_internodealigned_in_smp; ``` | 字段| 描述 | | :------- | ----: | -free_pages:未分配使用的page的数量。 -pages_min, pages_low and pages_high: zone对page管理调度的一些参数,下面章节将讲到。 -lowmem_reserve[MAX_NR_ZONES]: 为了防止一些代码必须运行在低地址区域,所以事先保留一些低地址区域的内存。 -pageset[NR_CPUS]: page管理的数据结构对象,内部有一个page的列表(list)来管理。每个CPU维护一个page list,避免自旋锁的冲突。这个数组的大小和NR_CPUS(CPU的数量)有关,这个值是编译的时候确定的。 -lock: 对zone并发访问的保护的自旋锁 -free_area: 页面使用状态的信息,以每个bit标识对应的page是否可以分配 -lru_lock: LRU(最近最少使用算法)的自旋锁 -reclaim_in_progress: 回收操作的原子锁 -active_list: 活跃的page的list -inactive_list: 不活跃的page的list -refill_counter:从活跃的page list中移除的page的数量 -nr_active: 活跃的page的数量 -nr_inactive: 不活跃的page的数量 -pressure: 检查回收page的指标 -all_unreclaimable: 如果检测2次还是不能回收zone的page的话,则设置为1 -pages_scanned: 上次回收page后,扫描过的page的数量。 -wait_table:等待一个page释放的等待队列哈希表。它会被wait_on_page(),unlock_page()函数使用. 用哈希表,而不用一个等待队列的原因,防止进程长期等待资源。 -wait_table_hash_nr_entries: 哈希表中的等待队列的数量 -zone_pgdat: 指向这个zone所在的pglist_data对象。 -zone_start_pfn: 和node_start_pfn的含义一样。这个成员是用于表示zone中的开始那个page在物理内存中的位置的present_pages, spanned_pages: 和node中的类似的成员含义一样。 -zone: zone的名字,字符串表示: "DMA","Normal" 和"HighMem" -ZONE_PADDING: 由于自旋锁频繁的被使用,因此为了性能上的考虑,将某些成员对齐到cache line中,有助于提高执行的性能。使用这个宏,可以确定zone->lock,zone->lru_lock,zone->pageset这些成员使用不同的cache line. +| free_pages | 未分配使用的page的数量 | + +| lowmem_reserve[MAX_NR_ZONES] | 为了防止一些代码必须运行在低地址区域,所以事先保留一些低地址区域的内存 | +| pageset | page管理的数据结构对象,内部有一个page的列表(list)来管理。每个CPU维护一个page list,避免自旋锁的冲突。这个数组的大小和NR_CPUS(CPU的数量)有关,这个值是编译的时候确定的 | +| lock | 对zone并发访问的保护的自旋锁 | +| free_area[MAX_ORDER] | 页面使用状态的信息,以每个bit标识对应的page是否可以分配 | +| lru_lock | LRU(最近最少使用算法)的自旋锁 | +| reclaim_in_progress | 回收操作的原子锁 | +| active_list | 活跃的page的list | +| inactive_list | 不活跃的page的list | +| refill_counter | 从活跃的page list中移除的page的数量 +| nr_active | 活跃的page的数量 | +| nr_inactive | 不活跃的page的数量 | +| pressure | 检查回收page的指标 | +| all_unreclaimable | 如果检测2次还是不能回收zone的page的话,则设置为1 | +| pages_scanned | 上次回收page后,扫描过的page的数量 | +| wait_table:等待一个page释放的等待队列哈希表。它会被| wait_on_page(),unlock_page()函数使用. 用哈希表,而不用一个等待队列的原因,防止进程长期等待资源。 +| wait_table_hash_nr_entries | 哈希表中的等待队列的数量 +| zone_pgdat | 指向这个zone所在的pglist_data对象 | +| zone_start_pfn | 和node_start_pfn的含义一样。这个成员是用于表示zone中的开始那个page在物理内存中的位置的present_pages, spanned_pages: 和node中的类似的成员含义一样 | +| name | zone的名字,字符串表示: "DMA","Normal" 和"HighMem" | +| ZONE_PADDING | 由于自旋锁频繁的被使用,因此为了性能上的考虑,将某些成员对齐到cache line中,有助于提高执行的性能。使用这个宏,可以确定zone->lock,zone->lru_lock,zone->pageset这些成员使用不同的cache line. | + Zone的管理调度的一些参数: (Zone watermarks), -英文直译为zone的水平,打个比喻,就像一个水库,水存量很小的时候加大进水量,水存量达到一个标准的时候,减小进水量,当快要满的时候,可能就关闭了进水口。pages_min, pages_low and pages_high就类似与这个标准。 -当系统中可用内存很少的时候,系统代码kswapd被唤醒,开始回收释放page。pages_min, pages_low and pages_high这些参数影响着这个代码的行为。 -每个zone有三个水平标准:pages_min, pages_low and pages_high,帮助确定zone中内存分配使用的压力状态。kswapd和这3个参数的互动关系如下图: +英文直译为zone的水平,打个比喻,就像一个水库,水存量很小的时候加大进水量,水存量达到一个标准的时候,减小进水量,当快要满的时候,可能就关闭了进水口。 + +pages_min, pages_low and pages_high就类似与这个标准 + +当系统中可用内存很少的时候,系统代码kswapd被唤醒,开始回收释放page + +pages_min, pages_low and pages_high这些参数影响着这个代码的行为。 + +每个zone有三个水`平标准:pages_min, pages_low and pages_high,帮助确定zone中内存分配使用的压力状态。kswapd和这3个参数的互动关系如下图: page_min中所表示的page的数量值,是在内存初始化的过程中调用free_area_init_core()中计算的。这个数值是根据zone中的page的数量除以一个>1的系数来确定的。通常是这样初始化的ZoneSizeInPages/128。 page_low: 当空闲页面的数量达到page_low所标定的数量的时候,kswapd线程将被唤醒,并开始释放回收页面。这个值默认是page_min的2倍。 @@ -488,4 +354,13 @@ Zone的初始化 在kernel page table通过paging_init()函数完全建立起z来以后,zone被初始化。下面章节将描述这个。当然不同的体系结构这个过程肯定也是不一样的,但它们的目的却是相同的:确定什么参数需要传递给free_area_init()函数(对于UMA体系结构)或者free_area_init_node()函数(对于NUMA体系结构)。这里省略掉NUMA体系结构的说明。 free_area_init()函数的参数: unsigned long *zones_sizes: 系统中每个zone所管理的page的数量的数组。这个时候,还没能确定zone中那些page是可以分配使用的(free)。这个信息知道boot memory allocator完成之前还无法知道。 -来源: http://www.uml.org.cn/embeded/201208071.asp \ No newline at end of file +来源: http://www.uml.org.cn/embeded/201208071.asp + + + +#页面page +------- + + +#页表 +-------