Linux内存描述之概述--Linux内存管理(一)
1 前景回顧
1.1 UMA和NUMA兩種模型
共享存儲型多處理機(jī)有兩種模型
- 均勻存儲器存取(Uniform-Memory-Access,簡稱UMA)模型
將可用內(nèi)存以連續(xù)方式組織起來,
- 非均勻存儲器存取(Nonuniform-Memory-Access,簡稱NUMA)模型
1.2 UMA模型
傳統(tǒng)的多核運算是使用SMP(Symmetric Multi-Processor )模式:將多個處理器與一個集中的存儲器和I/O總線相連。所有處理器只能訪問同一個物理存儲器,因此SMP系統(tǒng)有時也被稱為一致存儲器訪問(UMA)結(jié)構(gòu)體系,一致性意指無論在什么時候,處理器只能為內(nèi)存的每個數(shù)據(jù)保持或共享唯一一個數(shù)值。
物理存儲器被所有處理機(jī)均勻共享。所有處理機(jī)對所有存儲字具有相同的存取時間,這就是為什么稱它為均勻存儲器存取的原因。每臺處理機(jī)可以有私用高速緩存,外圍設(shè)備也以一定形式共享。
很顯然,SMP的缺點是可伸縮性有限,因為在存儲器和I/O接口達(dá)到飽和的時候,增加處理器并不能獲得更高的性能,與之相對應(yīng)的有AMP架構(gòu),不同核之間有主從關(guān)系,如一個核控制另外一個核的業(yè)務(wù),可以理解為多核系統(tǒng)中控制平面和數(shù)據(jù)平面。
1.3 NUMA模型
NUMA模式是一種分布式存儲器訪問方式,處理器可以同時訪問不同的存儲器地址,大幅度提高并行性。 NUMA總是多處理器計算機(jī),系統(tǒng)的哪個CPU都有本地內(nèi)存, 可支持快速的訪問, 各個處理器之前通過總線鏈接起來, 以支持堆其他CPU的本地內(nèi)存的訪問, 當(dāng)然訪問要比本地內(nèi)存慢.
NUMA模式下,處理器被劃分成多個”節(jié)點”(node), 每個節(jié)點被分配有的本地存儲器空間。 所有節(jié)點中的處理器都可以訪問全部的系統(tǒng)物理存儲器,但是訪問本節(jié)點內(nèi)的存儲器所需要的時間,比訪問某些遠(yuǎn)程節(jié)點內(nèi)的存儲器所花的時間要少得多。
其訪問時間隨存儲字的位置不同而變化。其共享存儲器物理上是分布在所有處理機(jī)的本地存儲器上。所有本地存儲器的集合組成了全局地址空間,可被所有的處理機(jī)訪問。處理機(jī)訪問本地存儲器是比較快的,但訪問屬于另一臺處理機(jī)的遠(yuǎn)程存儲器則比較慢,因為通過互連網(wǎng)絡(luò)會產(chǎn)生附加時延
NUMA 的主要優(yōu)點是伸縮性。NUMA 體系結(jié)構(gòu)在設(shè)計上已超越了 SMP 體系結(jié)構(gòu)在伸縮性上的限制。通過 SMP,所有的內(nèi)存訪問都傳遞到相同的共享內(nèi)存總線。這種方式非常適用于 CPU 數(shù)量相對較少的情況,但不適用于具有幾十個甚至幾百個 CPU 的情況,因為這些 CPU 會相互競爭對共享內(nèi)存總線的訪問。NUMA 通過限制任何一條內(nèi)存總線上的 CPU 數(shù)量并依靠高速互連來連接各個節(jié)點,從而緩解了這些瓶頸狀況。
2 (N)UMA模型中l(wèi)inux內(nèi)存的機(jī)構(gòu)
Linux適用于各種不同的體系結(jié)構(gòu), 而不同體系結(jié)構(gòu)在內(nèi)存管理方面的差別很大. 因此linux內(nèi)核需要用一種體系結(jié)構(gòu)無關(guān)的方式來表示內(nèi)存.
Linux內(nèi)核通過插入一些兼容層, 使得不同體系結(jié)構(gòu)的差異很好的被隱藏起來, 內(nèi)核對一致和非一致內(nèi)存訪問使用相同的數(shù)據(jù)結(jié)構(gòu)
2.1 (N)UMA模型中l(wèi)inux內(nèi)存的機(jī)構(gòu)
非一致存儲器訪問(NUMA)模式下
- 處理器被劃分成多個”節(jié)點”(node), 每個節(jié)點被分配有的本地存儲器空間. 所有節(jié)點中的處理器都可以訪問全部的系統(tǒng)物理存儲器,但是訪問本節(jié)點內(nèi)的存儲器所需要的時間,比訪問某些遠(yuǎn)程節(jié)點內(nèi)的存儲器所花的時間要少得多
- 內(nèi)存被分割成多個區(qū)域(BANK,也叫”簇”),依據(jù)簇與處理器的”距離”不同, 訪問不同簇的代碼也會不同. 比如,可能把內(nèi)存的一個簇指派給每個處理器,或則某個簇和設(shè)備卡很近,很適合DMA,那么就指派給該設(shè)備。因此當(dāng)前的多數(shù)系統(tǒng)會把內(nèi)存系統(tǒng)分割成2塊區(qū)域,一塊是專門給CPU去訪問,一塊是給外圍設(shè)備板卡的DMA去訪問
在UMA系統(tǒng)中, 內(nèi)存就相當(dāng)于一個只使用一個NUMA節(jié)點來管理整個系統(tǒng)的內(nèi)存. 而內(nèi)存管理的其他地方則認(rèn)為他們就是在處理一個(偽)NUMA系統(tǒng).
2.2 Linux物理內(nèi)存的組織形式
Linux把物理內(nèi)存劃分為三個層次來管理
| 存儲節(jié)點(Node) | CPU被劃分為多個節(jié)點(node), 內(nèi)存則被分簇, 每個CPU對應(yīng)一個本地物理內(nèi)存, 即一個CPU-node對應(yīng)一個內(nèi)存簇bank,即每個內(nèi)存簇被認(rèn)為是一個節(jié)點 |
| 管理區(qū)(Zone) | 每個物理內(nèi)存節(jié)點node被劃分為多個內(nèi)存管理區(qū)域, 用于表示不同范圍的內(nèi)存, 內(nèi)核可以使用不同的映射方式映射物理內(nèi)存 |
| 頁面(Page) | 內(nèi)存被細(xì)分為多個頁面幀, 頁面是最基本的頁面分配的單位 |
為了支持NUMA模型,也即CPU對不同內(nèi)存單元的訪問時間可能不同,此時系統(tǒng)的物理內(nèi)存被劃分為幾個節(jié)點(node), 一個node對應(yīng)一個內(nèi)存簇bank,即每個內(nèi)存簇被認(rèn)為是一個節(jié)點
- 首先, 內(nèi)存被劃分為結(jié)點. 每個節(jié)點關(guān)聯(lián)到系統(tǒng)中的一個處理器, 內(nèi)核中表示為pg_data_t的實例. 系統(tǒng)中每個節(jié)點被鏈接到一個以NULL結(jié)尾的pgdat_list鏈表中<而其中的每個節(jié)點利用pg_data_tnode_next字段鏈接到下一節(jié).而對于PC這種UMA結(jié)構(gòu)的機(jī)器來說, 只使用了一個成為contig_page_data的靜態(tài)pg_data_t結(jié)構(gòu).
- 接著各個節(jié)點又被劃分為內(nèi)存管理區(qū)域, 一個管理區(qū)域通過struct zone_struct描述, 其被定義為zone_t, 用以表示內(nèi)存的某個范圍, 低端范圍的16MB被描述為ZONE_DMA, 某些工業(yè)標(biāo)準(zhǔn)體系結(jié)構(gòu)中的(ISA)設(shè)備需要用到它, 然后是可直接映射到內(nèi)核的普通內(nèi)存域ZONE_NORMAL,最后是超出了內(nèi)核段的物理地址域ZONE_HIGHMEM, 被稱為高端內(nèi)存. 是系統(tǒng)中預(yù)留的可用內(nèi)存空間, 不能被內(nèi)核直接映射.
- 最后頁幀(page frame)代表了系統(tǒng)內(nèi)存的最小單位, 堆內(nèi)存中的每個頁都會創(chuàng)建一個struct page的一個實例. 傳統(tǒng)上,把內(nèi)存視為連續(xù)的字節(jié),即內(nèi)存為字節(jié)數(shù)組,內(nèi)存單元的編號(地址)可作為字節(jié)數(shù)組的索引. 分頁管理時,將若干字節(jié)視為一頁,比如4K byte. 此時,內(nèi)存變成了連續(xù)的頁,即內(nèi)存為頁數(shù)組,每一頁物理內(nèi)存叫頁幀,以頁為單位對內(nèi)存進(jìn)行編號,該編號可作為頁數(shù)組的索引,又稱為頁幀號.
在一個單獨的節(jié)點內(nèi),任一給定CPU訪問頁面所需的時間都是相同的。然而,對不同的CPU,這個時間可能就不同。對每個CPU而言,內(nèi)核都試圖把耗時節(jié)點的訪問次數(shù)減到最少這就要小心地選擇CPU最常引用的內(nèi)核數(shù)據(jù)結(jié)構(gòu)的存放位置.
2.3 內(nèi)存節(jié)點node
CPU被劃分為多個節(jié)點(node), 內(nèi)存則被分簇, 每個CPU對應(yīng)一個本地物理內(nèi)存, 即一個CPU-node對應(yīng)一個內(nèi)存簇bank,即每個內(nèi)存簇被認(rèn)為是一個節(jié)點
系統(tǒng)的物理內(nèi)存被劃分為幾個節(jié)點(node), 一個node對應(yīng)一個內(nèi)存簇bank,即每個內(nèi)存簇被認(rèn)為是一個節(jié)點
在LINUX中引入一個數(shù)據(jù)結(jié)構(gòu)struct pglist_data ,來描述一個node,定義在include/linux/mmzone.h 文件中。(這個結(jié)構(gòu)被typedef pg_data_t)。
- 對于NUMA系統(tǒng)來講, 整個系統(tǒng)的內(nèi)存由一個node_data的pg_data_t指針數(shù)組來管理,
- 對于PC這樣的UMA系統(tǒng),使用struct pglist_data contig_page_data ,作為系統(tǒng)唯一的node管理所有的內(nèi)存區(qū)域。(UMA系統(tǒng)中中只有一個node)
可以使用NODE_DATA(node_id)來查找系統(tǒng)中編號為node_id的結(jié)點, 參見NODE_DATA的定義
UMA結(jié)構(gòu)下由于只有一個結(jié)點, 因此該宏總是返回全局的contig_page_data, 而與參數(shù)node_id無關(guān).
extern struct pglist_data *node_data[]; #define NODE_DATA(nid) (node_data[(nid)])在UMA結(jié)構(gòu)的機(jī)器中, 只有一個node結(jié)點即contig_page_data, 此時NODE_DATA直接指向了全局的contig_page_data, 而與node的編號nid無關(guān), 參照include/linux/mmzone.h?v=4.7, line 858, 其中全局唯一的內(nèi)存node結(jié)點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采用節(jié)點局部分配的策略, 從最靠近運行中的CPU的節(jié)點分配內(nèi)存, 由于進(jìn)程往往是在同一個CPU上運行, 因此從當(dāng)前節(jié)點得到的內(nèi)存很可能被用到
2.4 物理內(nèi)存區(qū)域zone
因為實際的計算機(jī)體系結(jié)構(gòu)有硬件的諸多限制, 這限制了頁框可以使用的方式. 尤其是, Linux內(nèi)核必須處理80x86體系結(jié)構(gòu)的兩種硬件約束.
- ISA總線的直接內(nèi)存存儲DMA處理器有一個嚴(yán)格的限制 : 他們只能對RAM的前16MB進(jìn)行尋址
- 在具有大容量RAM的現(xiàn)代32位計算機(jī)中, CPU不能直接訪問所有的物理地址, 因為線性地址空間太小, 內(nèi)核不可能直接映射所有物理內(nèi)存到線性地址空間, 我們會在后面典型架構(gòu)(x86)上內(nèi)存區(qū)域劃分詳細(xì)講解x86_32上的內(nèi)存區(qū)域劃分
因此Linux內(nèi)核對不同區(qū)域的內(nèi)存需要采用不同的管理方式和映射方式,
為了解決這些制約條件,Linux使用了三種區(qū):
而為了兼容一些設(shè)備的熱插拔支持以及內(nèi)存碎片化的處理, 內(nèi)核也引入一些邏輯上的內(nèi)存區(qū).
內(nèi)核將每個簇所對應(yīng)的node又被分成的稱為管理區(qū)(zone)的塊,它們各自描述在內(nèi)存中的范圍。一個管理區(qū)(zone)由struct zone結(jié)構(gòu)體來描述,在linux-2.4.37之前的內(nèi)核中是用typedef struct zone_struct zone_t數(shù)據(jù)結(jié)構(gòu)來描述)
對于x86_32的機(jī)器,管理區(qū)(內(nèi)存區(qū)域)類型如下分布
| ZONE_DMA | 0~15MB |
| ZONE_NORMAL | 16MB~895MB |
| ZONE_HIGHMEM | 896MB~物理內(nèi)存結(jié)束 |
內(nèi)核在初始化內(nèi)存管理區(qū)時, 首先建立管理區(qū)表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);該表處理起來就像一個多維數(shù)組,
- MAX_NR_ZONES是一個節(jié)點中所能包容納的管理區(qū)的最大數(shù), 如3個, 定義在include/linux/mmzone.h?v=2.4.37, line 25, 與zone區(qū)域的類型(ZONE_DMA, ZONE_NORMAL, ZONE_HIGHMEM)定義在一起. 當(dāng)然這時候我們這些標(biāo)識都是通過宏的方式來實現(xiàn)的, 而不是如今的枚舉類型
- MAX_NR_NODES是可以存在的節(jié)點的最大數(shù).
- 函數(shù)EXPORT_SYMBOL使得內(nèi)核的變量或者函數(shù)可以被載入的模塊(比如我們的驅(qū)動模塊)所訪問.
2.5 內(nèi)存頁page
大多數(shù)內(nèi)核(kernel)的操作只使用ZONE_NORMAL區(qū)域,系統(tǒng)內(nèi)存由很多固定大小的內(nèi)存塊組成的,這樣的內(nèi)存塊稱作為“頁”(PAGE),
x86體系結(jié)構(gòu)中,page的大小為4096個字節(jié)。
每個物理的頁由一個struct page的數(shù)據(jù)結(jié)構(gòu)對象來描述。頁的數(shù)據(jù)結(jié)構(gòu)對象都保存在mem_map全局?jǐn)?shù)組中,該數(shù)組通常被存放在ZONE_NORMAL的首部,或者就在小內(nèi)存系統(tǒng)中為裝入內(nèi)核映像而預(yù)留的區(qū)域之后。從載入內(nèi)核的低地址內(nèi)存區(qū)域的后面內(nèi)存區(qū)域,也就是ZONE_NORMAL開始的地方的內(nèi)存的頁的數(shù)據(jù)結(jié)構(gòu)對象,都保存在這個全局?jǐn)?shù)組中。
2.6 高端內(nèi)存
由于能夠被Linux內(nèi)核直接訪問的ZONE_NORMAL區(qū)域的內(nèi)存空間也是有限的,所以LINUX提出了高端內(nèi)存(High memory)的概念,并且允許對高端內(nèi)存的訪問
轉(zhuǎn)載于:https://www.cnblogs.com/linhaostudy/p/9986692.html
總結(jié)
以上是生活随笔為你收集整理的Linux内存描述之概述--Linux内存管理(一)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: React-Native Navigat
- 下一篇: oracle12c 新建表空间