1. 程式人生 > >Linux內存描述之概述--Linux內存管理(一)

Linux內存描述之概述--Linux內存管理(一)

-m 直接映射 symbol 引入 tro 所有 變量 訪問方式 類型

1 前景回顧

1.1 UMA和NUMA兩種模型

共享存儲型多處理機有兩種模型

  • 均勻存儲器存取(Uniform-Memory-Access,簡稱UMA)模型

將可用內存以連續方式組織起來,

  • 非均勻存儲器存取(Nonuniform-Memory-Access,簡稱NUMA)模型

1.2 UMA模型

傳統的多核運算是使用SMP(Symmetric Multi-Processor )模式:將多個處理器與一個集中的存儲器和I/O總線相連。所有處理器只能訪問同一個物理存儲器,因此SMP系統有時也被稱為一致存儲器訪問(UMA)結構體系,一致性意指無論在什麽時候,處理器只能為內存的每個數據保持或共享唯一一個數值。

物理存儲器被所有處理機均勻共享。所有處理機對所有存儲字具有相同的存取時間,這就是為什麽稱它為均勻存儲器存取的原因。每臺處理機可以有私用高速緩存,外圍設備也以一定形式共享。

很顯然,SMP的缺點是可伸縮性有限,因為在存儲器和I/O接口達到飽和的時候,增加處理器並不能獲得更高的性能,與之相對應的有AMP架構,不同核之間有主從關系,如一個核控制另外一個核的業務,可以理解為多核系統中控制平面和數據平面。

1.3 NUMA模型

NUMA模式是一種分布式存儲器訪問方式,處理器可以同時訪問不同的存儲器地址,大幅度提高並行性。 NUMA總是多處理器計算機,系統的哪個CPU都有本地內存, 可支持快速的訪問, 各個處理器之前通過總線鏈接起來, 以支持堆其他CPU的本地內存的訪問, 當然訪問要比本地內存慢.

NUMA模式下,處理器被劃分成多個”節點”(node), 每個節點被分配有的本地存儲器空間。 所有節點中的處理器都可以訪問全部的系統物理存儲器,但是訪問本節點內的存儲器所需要的時間,比訪問某些遠程節點內的存儲器所花的時間要少得多。

其訪問時間隨存儲字的位置不同而變化。其共享存儲器物理上是分布在所有處理機的本地存儲器上。所有本地存儲器的集合組成了全局地址空間,可被所有的處理機訪問。處理機訪問本地存儲器是比較快的,但訪問屬於另一臺處理機的遠程存儲器則比較慢,因為通過互連網絡會產生附加時延

NUMA 的主要優點是伸縮性。NUMA 體系結構在設計上已超越了 SMP 體系結構在伸縮性上的限制。通過 SMP,所有的內存訪問都傳遞到相同的共享內存總線。這種方式非常適用於 CPU 數量相對較少的情況,但不適用於具有幾十個甚至幾百個 CPU 的情況,因為這些 CPU 會相互競爭對共享內存總線的訪問。NUMA 通過限制任何一條內存總線上的 CPU 數量並依靠高速互連來連接各個節點,從而緩解了這些瓶頸狀況。

2 (N)UMA模型中linux內存的機構

Linux適用於各種不同的體系結構, 而不同體系結構在內存管理方面的差別很大. 因此linux內核需要用一種體系結構無關的方式來表示內存.

Linux內核通過插入一些兼容層, 使得不同體系結構的差異很好的被隱藏起來, 內核對一致和非一致內存訪問使用相同的數據結構

2.1 (N)UMA模型中linux內存的機構

非一致存儲器訪問(NUMA)模式下

  • 處理器被劃分成多個”節點”(node), 每個節點被分配有的本地存儲器空間. 所有節點中的處理器都可以訪問全部的系統物理存儲器,但是訪問本節點內的存儲器所需要的時間,比訪問某些遠程節點內的存儲器所花的時間要少得多
  • 內存被分割成多個區域(BANK,也叫”簇”),依據簇與處理器的”距離”不同, 訪問不同簇的代碼也會不同. 比如,可能把內存的一個簇指派給每個處理器,或則某個簇和設備卡很近,很適合DMA,那麽就指派給該設備。因此當前的多數系統會把內存系統分割成2塊區域,一塊是專門給CPU去訪問,一塊是給外圍設備板卡的DMA去訪問

在UMA系統中, 內存就相當於一個只使用一個NUMA節點來管理整個系統的內存. 而內存管理的其他地方則認為他們就是在處理一個(偽)NUMA系統.

2.2 Linux物理內存的組織形式

Linux把物理內存劃分為三個層次來管理

層次 描述
存儲節點(Node) CPU被劃分為多個節點(node), 內存則被分簇, 每個CPU對應一個本地物理內存, 即一個CPU-node對應一個內存簇bank,即每個內存簇被認為是一個節點
管理區(Zone) 每個物理內存節點node被劃分為多個內存管理區域, 用於表示不同範圍的內存, 內核可以使用不同的映射方式映射物理內存
頁面(Page) 內存被細分為多個頁面幀, 頁面是最基本的頁面分配的單位

為了支持NUMA模型,也即CPU對不同內存單元的訪問時間可能不同,此時系統的物理內存被劃分為幾個節點(node), 一個node對應一個內存簇bank,即每個內存簇被認為是一個節點

  • 首先, 內存被劃分為結點. 每個節點關聯到系統中的一個處理器, 內核中表示為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, 被稱為高端內存. 是系統中預留的可用內存空間, 不能被內核直接映射.
  • 最後頁幀(page frame)代表了系統內存的最小單位, 堆內存中的每個頁都會創建一個struct page的一個實例. 傳統上,把內存視為連續的字節,即內存為字節數組,內存單元的編號(地址)可作為字節數組的索引. 分頁管理時,將若幹字節視為一頁,比如4K byte. 此時,內存變成了連續的頁,即內存為頁數組,每一頁物理內存叫頁幀,以頁為單位對內存進行編號,該編號可作為頁數組的索引,又稱為頁幀號.

在一個單獨的節點內,任一給定CPU訪問頁面所需的時間都是相同的。然而,對不同的CPU,這個時間可能就不同。對每個CPU而言,內核都試圖把耗時節點的訪問次數減到最少這就要小心地選擇CPU最常引用的內核數據結構的存放位置.

2.3 內存節點node

CPU被劃分為多個節點(node), 內存則被分簇, 每個CPU對應一個本地物理內存, 即一個CPU-node對應一個內存簇bank,即每個內存簇被認為是一個節點

系統的物理內存被劃分為幾個節點(node), 一個node對應一個內存簇bank,即每個內存簇被認為是一個節點

在LINUX中引入一個數據結構struct pglist_data ,來描述一個node,定義在include/linux/mmzone.h 文件中。(這個結構被typedef pg_data_t)。

  • 對於NUMA系統來講, 整個系統的內存由一個node_data的pg_data_t指針數組來管理,
  • 對於PC這樣的UMA系統,使用struct pglist_data contig_page_data ,作為系統唯一的node管理所有的內存區域。(UMA系統中中只有一個node)

可以使用NODE_DATA(node_id)來查找系統中編號為node_id的結點, 參見NODE_DATA的定義

UMA結構下由於只有一個結點, 因此該宏總是返回全局的contig_page_data, 而與參數node_id無關.

extern struct pglist_data *node_data[];
#define NODE_DATA(nid)          (node_data[(nid)])

在UMA結構的機器中, 只有一個node結點即contig_page_data, 此時NODE_DATA直接指向了全局的contig_page_data, 而與node的編號nid無關, 參照include/linux/mmzone.h?v=4.7, line 858, 其中全局唯一的內存node結點contig_page_data定義在mm/nobootmem.c?v=4.7, line 27, linux-2.4.37

#ifndef CONFIG_NEED_MULTIPLE_NODES
extern struct pglist_data contig_page_data;
#define NODE_DATA(nid)          (&contig_page_data)
#define NODE_MEM_MAP(nid)       mem_map
else
/*  ......  */
#endif

在分配一個頁面時, Linux采用節點局部分配的策略, 從最靠近運行中的CPU的節點分配內存, 由於進程往往是在同一個CPU上運行, 因此從當前節點得到的內存很可能被用到

2.4 物理內存區域zone

因為實際的計算機體系結構有硬件的諸多限制, 這限制了頁框可以使用的方式. 尤其是, Linux內核必須處理80x86體系結構的兩種硬件約束.

  • ISA總線的直接內存存儲DMA處理器有一個嚴格的限制 : 他們只能對RAM的前16MB進行尋址
  • 在具有大容量RAM的現代32位計算機中, CPU不能直接訪問所有的物理地址, 因為線性地址空間太小, 內核不可能直接映射所有物理內存到線性地址空間, 我們會在後面典型架構(x86)上內存區域劃分詳細講解x86_32上的內存區域劃分

因此Linux內核對不同區域的內存需要采用不同的管理方式和映射方式,

為了解決這些制約條件,Linux使用了三種區:

  1. ZONE_DMA : 這個區包含的頁用來執行DMA操作。
  2. ZONE_NOMAL : 這個區包含的都是能正常映射的頁。
  3. ZONE_HIGHEM : 這個區包”高端內存”,其中的頁能不永久地映射到內核地址空間

而為了兼容一些設備的熱插拔支持以及內存碎片化的處理, 內核也引入一些邏輯上的內存區.

內核將每個簇所對應的node又被分成的稱為管理區(zone)的塊,它們各自描述在內存中的範圍。一個管理區(zone)由struct zone結構體來描述,在linux-2.4.37之前的內核中是用typedef struct zone_struct zone_t數據結構來描述)

對於x86_32的機器,管理區(內存區域)類型如下分布

類型 區域
ZONE_DMA 0~15MB
ZONE_NORMAL 16MB~895MB
ZONE_HIGHMEM 896MB~物理內存結束

內核在初始化內存管理區時, 首先建立管理區表zone_table. 參見mm/page_alloc.c?v=2.4.37, line 38

/*
 *
 * The zone_table array is used to look up the address of the
 * struct zone corresponding to a given zone number (ZONE_DMA,
 * ZONE_NORMAL, or ZONE_HIGHMEM).
 */
zone_t *zone_table[MAX_NR_ZONES*MAX_NR_NODES];
EXPORT_SYMBOL(zone_table);

該表處理起來就像一個多維數組,

  • MAX_NR_ZONES是一個節點中所能包容納的管理區的最大數, 如3個, 定義在include/linux/mmzone.h?v=2.4.37, line 25, 與zone區域的類型(ZONE_DMA, ZONE_NORMAL, ZONE_HIGHMEM)定義在一起. 當然這時候我們這些標識都是通過宏的方式來實現的, 而不是如今的枚舉類型
  • MAX_NR_NODES是可以存在的節點的最大數.
  • 函數EXPORT_SYMBOL使得內核的變量或者函數可以被載入的模塊(比如我們的驅動模塊)所訪問.

2.5 內存頁page

大多數內核(kernel)的操作只使用ZONE_NORMAL區域,系統內存由很多固定大小的內存塊組成的,這樣的內存塊稱作為“頁”(PAGE),

x86體系結構中,page的大小為4096個字節。

每個物理的頁由一個struct page的數據結構對象來描述。頁的數據結構對象都保存在mem_map全局數組中,該數組通常被存放在ZONE_NORMAL的首部,或者就在小內存系統中為裝入內核映像而預留的區域之後。從載入內核的低地址內存區域的後面內存區域,也就是ZONE_NORMAL開始的地方的內存的頁的數據結構對象,都保存在這個全局數組中。

2.6 高端內存

由於能夠被Linux內核直接訪問的ZONE_NORMAL區域的內存空間也是有限的,所以LINUX提出了高端內存(High memory)的概念,並且允許對高端內存的訪問

Linux內存描述之概述--Linux內存管理(一)