一.背景
所謂物理內(nèi)存,就是安裝在機(jī)器上的,實(shí)打?qū)嵉膬?nèi)存設(shè)備(不包括硬件cache),被CPU通過總線訪問。在多核系統(tǒng)中,如果物理內(nèi)存對(duì)所有CPU來說沒有區(qū)別,每個(gè)CPU訪問內(nèi)存的方式也一樣,則這種體系結(jié)構(gòu)被稱為Uniform Memory Access(UMA)。
如果物理內(nèi)存是分布式的,由多個(gè)cell組成(比如每個(gè)核有自己的本地內(nèi)存),那么CPU在訪問靠近它的本地內(nèi)存的時(shí)候就比較快,訪問其他CPU的內(nèi)存或者全局內(nèi)存的時(shí)候就比較慢,這種體系結(jié)構(gòu)被稱為Non-Uniform Memory Access(NUMA)。
以上是硬件層面上的NUMA,而作為軟件層面的Linux,則對(duì)NUMA的概念進(jìn)行了抽象。即便硬件上是一整塊連續(xù)內(nèi)存的UMA,Linux也可將其劃分為若干的node。同樣,即便硬件上是物理內(nèi)存不連續(xù)的NUMA,Linux也可將其視作UMA。
所以,在Linux系統(tǒng)中,你可以基于一個(gè)UMA的平臺(tái)測(cè)試NUMA上的應(yīng)用特性。從另一個(gè)角度,UMA就是只有一個(gè)node的特殊NUMA,所以兩者可以統(tǒng)一用NUMA模型表示。
傳統(tǒng)的SMP(對(duì)稱多處理器)中,所有處理器都共享系統(tǒng)總線,因此當(dāng)處理器的數(shù)目增大時(shí),系統(tǒng)總線的競(jìng)爭沖突加大,系統(tǒng)總線將成為瓶頸,所以目前SMP系統(tǒng)的CPU數(shù)目一般只有數(shù)十個(gè),可擴(kuò)展能力受到極大限制。NUMA技術(shù)有效結(jié)合了SMP系統(tǒng)易編程性和MPP(大規(guī)模并行)系統(tǒng)易擴(kuò)展性的特點(diǎn),較好解決了SMP系統(tǒng)的可擴(kuò)展性問題,已成為當(dāng)今高性能服務(wù)器的主流體系結(jié)構(gòu)之一。
在NUMA系統(tǒng)中,當(dāng)Linux內(nèi)核收到內(nèi)存分配的請(qǐng)求時(shí),它會(huì)優(yōu)先從發(fā)出請(qǐng)求的CPU本地或鄰近的內(nèi)存node中尋找空閑內(nèi)存,這種方式被稱作local allocation,local allocation能讓接下來的內(nèi)存訪問相對(duì)底層的物理資源是local的。
每個(gè)node由一個(gè)或多個(gè)zone組成(我們可能經(jīng)常在各種對(duì)虛擬內(nèi)存和物理內(nèi)存的描述中迷失,但以后你見到zone,就知道指的是物理內(nèi)存),每個(gè)zone又由若干page frames組成(一般page frame都是指物理頁面)。
基于NUMA架構(gòu)的高性能服務(wù)器有HP的Superdome、SGI的Altix 3000、IBM的 x440、NEC的TX7、AMD的Opteron等。
概念
NUMA具有多個(gè)節(jié)點(diǎn)(Node),每個(gè)節(jié)點(diǎn)可以擁有多個(gè)CPU(每個(gè)CPU可以具有多個(gè)核或線程),節(jié)點(diǎn)內(nèi)使用共有的內(nèi)存控制器,因此節(jié)點(diǎn)的所有內(nèi)存對(duì)于本節(jié)點(diǎn)的所有CPU都是等同的,而對(duì)于其它節(jié)點(diǎn)中的所有CPU都是不同的。節(jié)點(diǎn)可分為本地節(jié)點(diǎn)(Local Node)、鄰居節(jié)點(diǎn)(Neighbour Node)和遠(yuǎn)端節(jié)點(diǎn)(Remote Node)三種類型。
本地節(jié)點(diǎn):對(duì)于某個(gè)節(jié)點(diǎn)中的所有CPU,此節(jié)點(diǎn)稱為本地節(jié)點(diǎn);
鄰居節(jié)點(diǎn):與本地節(jié)點(diǎn)相鄰的節(jié)點(diǎn)稱為鄰居節(jié)點(diǎn);
遠(yuǎn)端節(jié)點(diǎn):非本地節(jié)點(diǎn)或鄰居節(jié)點(diǎn)的節(jié)點(diǎn),稱為遠(yuǎn)端節(jié)點(diǎn)。
鄰居節(jié)點(diǎn)和遠(yuǎn)端節(jié)點(diǎn),稱作非本地節(jié)點(diǎn)(Off Node)。
CPU訪問不同類型節(jié)點(diǎn)內(nèi)存的速度是不相同的:本地節(jié)點(diǎn)>鄰居節(jié)點(diǎn)>遠(yuǎn)端節(jié)點(diǎn)。訪問本地節(jié)點(diǎn)的速度最快,訪問遠(yuǎn)端節(jié)點(diǎn)的速度最慢,即訪問速度與節(jié)點(diǎn)的距離有關(guān),距離越遠(yuǎn)訪問速度越慢,此距離稱作Node Distance。
常用的NUMA系統(tǒng)中:硬件設(shè)計(jì)已保證系統(tǒng)中所有的Cache是一致的(Cache Coherent, ccNUMA);不同類型節(jié)點(diǎn)間的Cache同步時(shí)間不一樣,會(huì)導(dǎo)致資源競(jìng)爭不公平,對(duì)于某些特殊的應(yīng)用,可以考慮使用FIFO Spinlock保證公平性。
二.NUMA存儲(chǔ)管理
NUMA系統(tǒng)是由多個(gè)結(jié)點(diǎn)通過高速互連網(wǎng)絡(luò)連接而成的,如圖1是SGI Altix 3000 ccNUMA系統(tǒng)中的兩個(gè)結(jié)點(diǎn)。
NUMA系統(tǒng)的結(jié)點(diǎn)通常是由一組CPU(如,SGI Altix 3000是2個(gè)Itanium2 CPU)和本地內(nèi)存組成,有的結(jié)點(diǎn)可能還有I/O子系統(tǒng)。由于每個(gè)結(jié)點(diǎn)都有自己的本地內(nèi)存,因此全系統(tǒng)的內(nèi)存在物理上是分布的,每個(gè)結(jié)點(diǎn)訪問本地內(nèi)存和訪問其它結(jié)點(diǎn)的遠(yuǎn)地內(nèi)存的延遲是不同的,為了減少非一致性訪存對(duì)系統(tǒng)的影響,在硬件設(shè)計(jì)時(shí)應(yīng)盡量降低遠(yuǎn)地內(nèi)存訪存延遲(如通過Cache一致性設(shè)計(jì)等),而操作系統(tǒng)也必須能感知硬件的拓?fù)浣Y(jié)構(gòu),優(yōu)化系統(tǒng)的訪存。
目前IA64 Linux所支持的NUMA架構(gòu)服務(wù)器的物理拓?fù)涿枋鍪峭ㄟ^ACPI(Advanced Configuration and Power Interface)實(shí)現(xiàn)的。ACPI是由Compaq、Intel、Microsoft、Phoenix和Toshiba聯(lián)合制定的BIOS規(guī)范,它定義了一個(gè)非常廣泛的配置和電源管理,目前該規(guī)范的版本已發(fā)展到2.0,3.0o版本正在制定中,具體信息可以從 http://www.acpi.info網(wǎng)站上獲得。ACPI規(guī)范也已廣泛應(yīng)用于IA-32架構(gòu)的至強(qiáng)服務(wù)器系統(tǒng)中。
Linux對(duì)NUMA系統(tǒng)的物理內(nèi)存分布信息是從系統(tǒng)firmware的ACPI表中獲得的,最重要的是SRAT(System Resource Affinity Table)和SLIT(System Locality Information Table)表,其中SRAT包含兩個(gè)結(jié)構(gòu):
- Processor Local APIC/SAPIC Affinity Structure:記錄某個(gè)CPU的信息;
- Memory Affinity Structure:記錄內(nèi)存的信息;
SLIT表則記錄了各個(gè)結(jié)點(diǎn)之間的距離,在系統(tǒng)中由數(shù)組node_distance[ ]記錄。
Linux采用Node、Zone和頁三級(jí)結(jié)構(gòu)來描述物理內(nèi)存的,如圖2所示,
圖2 Linux中Node、Zone和頁的關(guān)系
2.1 結(jié)點(diǎn)
Linux用一個(gè)struct pg_data_t結(jié)構(gòu)來描述系統(tǒng)的內(nèi)存,系統(tǒng)中每個(gè)結(jié)點(diǎn)都掛接在一個(gè)pgdat_list列表中,對(duì)UMA體系結(jié)構(gòu),則只有一個(gè)靜態(tài)的pg_data_t結(jié)構(gòu)contig_page_data。對(duì)NUMA系統(tǒng)來說則非常容易擴(kuò)充,NUMA系統(tǒng)中一個(gè)結(jié)點(diǎn)可以對(duì)應(yīng)Linux存儲(chǔ)描述中的一個(gè)結(jié)點(diǎn),具體描述見linux/mmzone.h。
typedef struct pglist_data {
zone_t node_zones[MAX_NR_ZONES];
zonelist_t node_zonelists[GFP_ZONEMASK+1];
int nr_zones;
struct page *node_mem_map;
unsigned long *valid_addr_bitmap;
struct bootmem_data *bdata;
unsigned long node_start_paddr;
unsigned long node_start_mapnr;
unsigned long node_size;
int node_id;
struct pglist_data *node_next;
} pg_data_t;
下面就該結(jié)構(gòu)中的主要域進(jìn)行說明,
域 | 說明 |
---|---|
Node_zones | 該結(jié)點(diǎn)的zone類型,一般包括ZONE_HIGHMEM、ZONE_NORMAL和ZONE_DMA三類 |
Node_zonelists | 分配時(shí)內(nèi)存時(shí)zone的排序。它是由free_area_init_core()通過page_alloc.c中的build_zonelists()設(shè)置zone的順序 |
nr_zones | 該結(jié)點(diǎn)的 zone 個(gè)數(shù),可以從 1 到 3,但并不是所有的結(jié)點(diǎn)都需要有 3 個(gè) zone |
node_mem_map | 它是 struct page 數(shù)組的第一頁,該數(shù)組表示結(jié)點(diǎn)中的每個(gè)物理頁框。根據(jù)該結(jié)點(diǎn)在系統(tǒng)中的順序,它可在全局 mem_map 數(shù)組中的某個(gè)位置 |
Valid_addr_bitmap | 用于描述結(jié)點(diǎn)內(nèi)存空洞的位圖 |
node_start_paddr | 該結(jié)點(diǎn)的起始物理地址 |
node_start_mapnr | 給出在全局 mem_map 中的頁偏移,在free_area_init_core() 計(jì)算在 mem_map 和 lmem_map 之間的該結(jié)點(diǎn)的頁框數(shù)目 |
node_size | 該 zone 內(nèi)的頁框總數(shù) |
node_id | 該結(jié)點(diǎn)的 ID,全系統(tǒng)結(jié)點(diǎn) ID 從 0 開始 |
系統(tǒng)中所有結(jié)點(diǎn)都維護(hù)在 pgdat_list 列表中,在 init_bootmem_core 函數(shù)中完成該列表初始化工作。
影響zonelist方式
采用Node方式組織的zonelist為:
即各節(jié)點(diǎn)按照與本節(jié)點(diǎn)的Node Distance距離大小來排序,以達(dá)到更優(yōu)的內(nèi)存分配。
zonelist[2]
配置NUMA后,每個(gè)節(jié)點(diǎn)將關(guān)聯(lián)2個(gè)zonelist:
- zonelist[0]中存放以Node方式或Zone方式組織的zonelist,包括所有節(jié)點(diǎn)的zone;
- zonelist[1]中只存放本節(jié)點(diǎn)的zone即Legacy方式;
zonelist[1]用來實(shí)現(xiàn)僅從節(jié)點(diǎn)自身zone中的內(nèi)存分配(參考__GFP_THISNODE標(biāo)志)。
Page Frame
雖然內(nèi)存訪問的最小單位是byte或者word,但MMU是以page為單位來查找頁表的,page也就成了Linux中內(nèi)存管理的重要單位。包括換出(swap out)、回收(relcaim)、映射等操作,都是以page為粒度的。
因此,描述page frame的struct page自然成為了內(nèi)核中一個(gè)使用頻率極高,非常重要的結(jié)構(gòu)體,來看下它是怎樣構(gòu)成的(為了講解需要并非最新內(nèi)核代碼):
struct page {
unsigned long flags;
atomic_t count;
atomic_t _mapcount;
struct list_head lru;
struct address_space *mapping;
unsigned long index;
...
}
- flags表示page frame的狀態(tài)或者屬性,包括和內(nèi)存回收相關(guān)的PG_active, PG_dirty, PG_writeback, PG_reserved, PG_locked, PG_highmem等。其實(shí)flags是身兼多職的,它還有其他用途,這將在下文中介紹到。
- count表示引用計(jì)數(shù)。當(dāng)count值為0時(shí),該page frame可被free掉;如果不為0,說明該page正在被某個(gè)進(jìn)程或者內(nèi)核使用,調(diào)用page_count()可獲得count值。
- _mapcount表示該page frame被映射的個(gè)數(shù),也就是多少個(gè)page table entry中含有這個(gè)page frame的PFN。
- lru是"least recently used"的縮寫,根據(jù)page frame的活躍程度(使用頻率),一個(gè)可回收的page frame要么掛在active_list雙向鏈表上,要么掛在inactive_list雙向鏈表上,以作為頁面回收的選擇依據(jù),lru中包含的就是指向所在鏈表中前后節(jié)點(diǎn)的指針(參考這篇文章)。
- 如果一個(gè)page是屬于某個(gè)文件的(也就是在page cache中),則mapping指向文件inode對(duì)應(yīng)的address_space(這個(gè)結(jié)構(gòu)體雖然叫address_space,但并不是進(jìn)程地址空間里的那個(gè)address space),index表示該page在文件內(nèi)的offset(以page size為單位)。
有了文件的inode和index,當(dāng)這個(gè)page的內(nèi)容需要和外部disk/flash上對(duì)應(yīng)的部分同步時(shí),才可以找到具體的文件位置。如果一個(gè)page是anonymous的,則mapping指向表示swap cache的swapper_space,此時(shí)index就是swapper_space內(nèi)的offset。
事實(shí)上,現(xiàn)在最新Linux版本的struct page實(shí)現(xiàn)中大量用到了union,也就是同一個(gè)元素在不同的場(chǎng)景下有不同的意義。這是因?yàn)槊總€(gè)page frame都需要一個(gè)struct page來描述,一個(gè)page frame占4KB,一個(gè)struct page占32字節(jié),那所有的struct page需要消耗的內(nèi)存占了整個(gè)系統(tǒng)內(nèi)存的32/4096,不到1%的樣子,說小也小,但一個(gè)擁有4GB物理內(nèi)存的系統(tǒng),光這一項(xiàng)的開銷最大就可達(dá)30多MB。
如果能在struct page里省下4個(gè)字節(jié),那就能省下4多MB的內(nèi)存空間,所以這個(gè)結(jié)構(gòu)體的設(shè)計(jì)必須非??季?,不能因?yàn)槎嘁环N場(chǎng)景的需要就在struct page中增加一個(gè)元素,而是應(yīng)該盡量采取復(fù)用的方式。
需要注意的是,struct page描述和管理的是這4KB的物理內(nèi)存,它并不關(guān)注這段內(nèi)存中的數(shù)據(jù)變化。
2.2 Zone
每個(gè)結(jié)點(diǎn)的內(nèi)存被分為多個(gè)塊,稱為zones,它表示內(nèi)存中一段區(qū)域。一個(gè)zone用struct_zone_t結(jié)構(gòu)描述,zone的類型主要有ZONE_DMA、ZONE_NORMAL和ZONE_HIGHMEM。ZONE_DMA位于低端的內(nèi)存空間,用于某些舊的ISA設(shè)備。
ZONE_NORMAL的內(nèi)存直接映射到Linux內(nèi)核線性地址空間的高端部分,許多內(nèi)核操作只能在ZONE_NORMAL中進(jìn)行。
因?yàn)橛布南拗疲瑑?nèi)核不能對(duì)所有的page frames采用同樣的處理方法,因此它將屬性相同的page frames歸到一個(gè)zone中。對(duì)zone的劃分與硬件相關(guān),對(duì)不同的處理器架構(gòu)是可能不一樣的。
比如在i386中,一些使用DMA的設(shè)備只能訪問016MB的物理空間,因此將016MB劃分為了ZONE_DMA。ZONE_HIGHMEM則是適用于要訪問的物理地址空間大于虛擬地址空間,不能建立直接映射的場(chǎng)景。除開這兩個(gè)特殊的zone,物理內(nèi)存中剩余的部分就是ZONE_NORMAL了。
例如,在X86中,zone的物理地址如下:
類型 | 地址范圍 |
---|---|
ZONE_DMA | 前16MB內(nèi)存 |
ZONE_NORMAL | 16MB - 896MB |
ZONE_HIGHMEM | 896 MB以上 |
Zone是用struct zone_t描述的,它跟蹤頁框使用、空閑區(qū)域和鎖等信息,具體描述如下:
typedef struct zone_struct {
spinlock_t lock;
unsigned long free_pages;
unsigned long pages_min, pages_low, pages_high;
int need_balance;
free_area_t free_area[MAX_ORDER];
wait_queue_head_t * wait_table;
unsigned long wait_table_size;
unsigned long wait_table_shift;
struct pglist_data *zone_pgdat;
struct page *zone_mem_map;
unsigned long zone_start_paddr;
unsigned long zone_start_mapnr;char *name;unsigned long size;
} zone_t;
在其他一些處理器架構(gòu)中,ZONE_DMA可能是不需要的,ZONE_HIGHMEM也可能沒有。比如在64位的x64中,因?yàn)閮?nèi)核虛擬地址空間足夠大,不再需要ZONE_HIGH映射,但為了區(qū)分使用32位地址的DMA應(yīng)用和使用64位地址的DMA應(yīng)用,64位系統(tǒng)中設(shè)置了ZONE_DMA32和ZONE_DMA。
所以,同樣的ZONE_DMA,對(duì)于32位系統(tǒng)和64位系統(tǒng)表達(dá)的意義是不同的,ZONE_DMA32則只對(duì)64位系統(tǒng)有意義,對(duì)32位系統(tǒng)就等同于ZONE_DMA,沒有單獨(dú)存在的意義。
此外,還有防止內(nèi)存碎片化的ZONE_MOVABLE和支持設(shè)備熱插拔的ZONE_DEVICE??赏ㄟ^“cat /proc/zoneinfo |grep Node”命令查看系統(tǒng)中包含的zones的種類。
[rongtao@toa ~]$ cat /proc/zoneinfo |grep Node
Node 0, zone DMA
Node 0, zone DMA32
[rongtao@toa ~]$
下面就該結(jié)構(gòu)中的主要域進(jìn)行說明,
當(dāng)系統(tǒng)中可用的內(nèi)存比較少時(shí),kswapd將被喚醒,并進(jìn)行頁交換。如果需要內(nèi)存的壓力非常大,進(jìn)程將同步釋放內(nèi)存。如前面所述,每個(gè)zone有三個(gè)閾值,稱為pages_low,pages_min和pages_high,用于跟蹤該zone的內(nèi)存壓力。pages_min的頁框數(shù)是由內(nèi)存初始化free_area_init_core函數(shù),根據(jù)該zone內(nèi)頁框的比例計(jì)算的,最小值為20頁,最大值一般為255頁。當(dāng)?shù)竭_(dá)pages_min時(shí),分配器將采用同步方式進(jìn)行kswapd的工作;當(dāng)空閑頁的數(shù)目達(dá)到pages_low時(shí),kswapd被buddy分配器喚醒,開始釋放頁;當(dāng)達(dá)到pages_high時(shí),kswapd將被喚醒,此時(shí)kswapd不會(huì)考慮如何平衡該zone,直到有pages_high空閑頁為止。一般情況下,pages_high缺省值是pages_min的3倍。
Linux存儲(chǔ)管理的這種層次式結(jié)構(gòu)可以將ACPI的SRAT和SLIT信息與Node、Zone實(shí)現(xiàn)有效的映射,從而克服了傳統(tǒng)Linux中平坦式結(jié)構(gòu)無法反映NUMA架構(gòu)的缺點(diǎn)。當(dāng)一個(gè)任務(wù)請(qǐng)求分配內(nèi)存時(shí),Linux采用局部結(jié)點(diǎn)分配策略,首先在自己的結(jié)點(diǎn)內(nèi)尋找空閑頁;如果沒有,則到相鄰的結(jié)點(diǎn)中尋找空閑頁;如果還沒有,則到遠(yuǎn)程結(jié)點(diǎn)中尋找空閑頁,從而在操作系統(tǒng)級(jí)優(yōu)化了訪存性能。
Zone雖然是用于管理物理內(nèi)存的,但zone與zone之間并沒有任何的物理分割,它只是Linux為了便于管理進(jìn)行的一種邏輯意義上的劃分。Zone在Linux中用struct zone表示(以下為了講解需要,調(diào)整了結(jié)構(gòu)體中元素的順序):
struct zone {
spinlock_t lock;
unsigned long spanned_pages;
unsigned long present_pages;
unsigned long nr_reserved_highatomic;
atomic_long_t managed_pages;
struct free_area free_area[MAX_ORDER];
unsigned long _watermark[NR_WMARK];
long lowmem_reserve[MAX_NR_ZONES];
atomic_long_t vm_stat[NR_VM_ZONE_STAT_ITEMS];
unsigned long zone_start_pfn;
struct pglist_data *zone_pgdat;
struct page *zone_mem_map;
...
}
- lock是用來防止并行訪問struct zone的spin lock,它只能保護(hù)struct zone這個(gè)結(jié)構(gòu)體哈,可不能保護(hù)整個(gè)zone里的所有pages。
- spanned_pages是這個(gè)zone含有的總的page frames數(shù)目。在某些體系結(jié)構(gòu)(比如Sparc)中,zone中可能存在沒有物理頁面的"holes",spanned_pages減去這些holes里的absent pages就是present_pages。
nr_reserved_highatomic是為某些場(chǎng)景預(yù)留的內(nèi)存,managed_pages是由buddy內(nèi)存分配系統(tǒng)管理的page frames數(shù)目,其實(shí)也就是present_pages減去reserved pages。
- free_area由free list空閑鏈表構(gòu)成,表示zone中還有多少空余可供分配的page frames。_watermark有min(mininum), low, high三種,可作為啟動(dòng)內(nèi)存回收的判斷標(biāo)準(zhǔn)
lowmem_reserve是給更高位的zones預(yù)留的內(nèi)存。vm_stat作為zone的內(nèi)存使用情況的統(tǒng)計(jì)信息,是“/proc/zoneinfo”的數(shù)據(jù)來源。
- zone_start_pfn是zone的起始物理頁面號(hào),zone_start_pfn+spanned_pages就是該zone的結(jié)束物理頁面號(hào)。zone_pgdat是指向這個(gè)zone所屬的node的。zone_mem_map指向由struct page構(gòu)成的mem_map數(shù)組。
因?yàn)閮?nèi)核對(duì)zone的訪問是很頻繁的,為了更好的利用硬件cache來提高訪問速度,struct zone中還有一些填充位,用于幫助結(jié)構(gòu)體元素的cache line對(duì)齊。這和struct page對(duì)內(nèi)存精打細(xì)算的使用形成了鮮明的對(duì)比,因?yàn)閦one的種類很有限,一個(gè)系統(tǒng)中一共也不會(huì)有多少個(gè)zones,struct zone這個(gè)結(jié)構(gòu)體的體積大點(diǎn)也沒有什么關(guān)系。
Node Distance
上節(jié)中的例子是以2個(gè)節(jié)點(diǎn)為例,如果有>2個(gè)節(jié)點(diǎn)存在,就需要考慮不同節(jié)點(diǎn)間的距離來安排節(jié)點(diǎn),例如以4個(gè)節(jié)點(diǎn)2個(gè)ZONE為例,各節(jié)點(diǎn)的布局(如4個(gè)XLP832物理CPU級(jí)聯(lián))值如下:
上圖中,Node0和Node2的Node Distance為25,Node1和Node3的Node Distance為25,其它的Node Distance為15。
三、NUMA調(diào)度器
NUMA系統(tǒng)中,由于局部內(nèi)存的訪存延遲低于遠(yuǎn)地內(nèi)存訪存延遲,因此將進(jìn)程分配到局部內(nèi)存附近的處理器上可極大優(yōu)化應(yīng)用程序的性能。Linux 2.4內(nèi)核中的調(diào)度器由于只設(shè)計(jì)了一個(gè)運(yùn)行隊(duì)列,可擴(kuò)展性較差,在SMP平臺(tái)表現(xiàn)一直不理想。當(dāng)運(yùn)行的任務(wù)數(shù)較多時(shí),多個(gè)CPU增加了系統(tǒng)資源的競(jìng)爭,限制了負(fù)載的吞吐率。在2.5內(nèi)核開發(fā)時(shí),Ingo Molnar寫了一個(gè)多隊(duì)列調(diào)度器,稱為O(1),從2.5.2開始O(1)調(diào)度器已集成到2.5內(nèi)核版本中。O(1)是多隊(duì)列調(diào)度器,每個(gè)處理器都有一條自己的運(yùn)行隊(duì)列,但由于O(1)調(diào)度器不能較好地感知NUMA系統(tǒng)中結(jié)點(diǎn)這層結(jié)構(gòu),從而不能保證在調(diào)度后該進(jìn)程仍運(yùn)行在同一個(gè)結(jié)點(diǎn)上,為此,Eirch Focht開發(fā)了結(jié)點(diǎn)親和的NUMA調(diào)度器,它是建立在Ingo Molnar的O(1)調(diào)度器基礎(chǔ)上的,Eirch將該調(diào)度器向后移植到2.4.X內(nèi)核中,該調(diào)度器最初是為基于IA64的NUMA機(jī)器的2.4內(nèi)核開發(fā)的,后來Matt Dobson將它移植到基于X86的NUMA-Q硬件上。
3.1 初始負(fù)載平衡
在每個(gè)任務(wù)創(chuàng)建時(shí)都會(huì)賦予一個(gè)HOME結(jié)點(diǎn)(所謂HOME結(jié)點(diǎn),就是該任務(wù)獲得最初內(nèi)存分配的結(jié)點(diǎn)),它是當(dāng)時(shí)創(chuàng)建該任務(wù)時(shí)全系統(tǒng)負(fù)載最輕的結(jié)點(diǎn),由于目前Linux中不支持任務(wù)的內(nèi)存從一個(gè)結(jié)點(diǎn)遷移到另一個(gè)結(jié)點(diǎn),因此在該任務(wù)的生命期內(nèi)HOME結(jié)點(diǎn)保持不變。一個(gè)任務(wù)最初的負(fù)載平衡工作(也就是選該任務(wù)的HOME結(jié)點(diǎn))缺省情況下是由exec()系統(tǒng)調(diào)用完成的,也可以由fork()系統(tǒng)調(diào)用完成。在任務(wù)結(jié)構(gòu)中的node_policy域決定了最初的負(fù)載平衡選擇方式。
Node_policy | 平衡方式 | 注釋 |
---|---|---|
0(缺省值) | do_execve() | 任務(wù)由fork()創(chuàng)建,但不在同一個(gè)結(jié)點(diǎn)上運(yùn)行exec() |
1 | do_fork() | 如果子進(jìn)程有新的mm結(jié)構(gòu),選擇新的HOME結(jié)點(diǎn) |
2 | do_fork() | 選擇新的HOME結(jié)點(diǎn) |
3.2 動(dòng)態(tài)負(fù)載平衡
在結(jié)點(diǎn)內(nèi),該NUMA調(diào)度器如同O(1)調(diào)度器一樣。在一個(gè)空閑處理器上的動(dòng)態(tài)負(fù)載平衡是由每隔1ms的時(shí)鐘中斷觸發(fā)的,它試圖尋找一個(gè)高負(fù)載的處理器,并將該處理器上的任務(wù)遷移到空閑處理器上。在一個(gè)負(fù)載較重的結(jié)點(diǎn),則每隔200ms觸發(fā)一次。調(diào)度器只搜索本結(jié)點(diǎn)內(nèi)的處理器,只有還沒有運(yùn)行的任務(wù)可以從Cache池中移動(dòng)到其它空閑的處理器。
如果本結(jié)點(diǎn)的負(fù)載均衡已經(jīng)非常好,則計(jì)算其它結(jié)點(diǎn)的負(fù)載情況。如果某個(gè)結(jié)點(diǎn)的負(fù)載超過本結(jié)點(diǎn)的25%,則選擇該結(jié)點(diǎn)進(jìn)行負(fù)載均衡。如果本地結(jié)點(diǎn)具有平均的負(fù)載,則延遲該結(jié)點(diǎn)的任務(wù)遷移;如果負(fù)載非常差,則延遲的時(shí)間非常短,延遲時(shí)間長短依賴于系統(tǒng)的拓?fù)浣Y(jié)構(gòu)。
四、CpuMemSets
SGI的Origin 3000 ccNUMA系統(tǒng)在許多領(lǐng)域得到了廣泛應(yīng)用,是個(gè)非常成功的系統(tǒng),為了優(yōu)化Origin 3000的性能,SGI的IRIX操作系統(tǒng)在其上實(shí)現(xiàn)了CpuMemSets,通過將應(yīng)用與CPU和內(nèi)存的綁定,充分發(fā)揮NUMA系統(tǒng)本地訪存的優(yōu)勢(shì)。Linux在NUMA項(xiàng)目中也實(shí)現(xiàn)了CpuMemSets,并且在SGI的Altix 3000的服務(wù)器中得到實(shí)際應(yīng)用。
CpuMemSets為Linux提供了系統(tǒng)服務(wù)和應(yīng)用在指定CPU上調(diào)度和在指定結(jié)點(diǎn)上分配內(nèi)存的機(jī)制。CpuMemSets是在已有的Linux調(diào)度和資源分配代碼基礎(chǔ)上增加了cpumemmap和cpumemset兩層結(jié)構(gòu),底層的cpumemmap層提供一個(gè)簡單的映射對(duì),主要功能是:將系統(tǒng)的CPU號(hào)映射到應(yīng)用的CPU號(hào)、將系統(tǒng)的內(nèi)存塊號(hào)映射到應(yīng)用的內(nèi)存塊號(hào);上層的cpumemset層主要功能是:指定一個(gè)進(jìn)程在哪些應(yīng)用CPU上調(diào)度任務(wù)、指定內(nèi)核或虛擬存儲(chǔ)區(qū)可分配哪些應(yīng)用內(nèi)存塊。
4.1 cpumemmap
內(nèi)核任務(wù)調(diào)度和內(nèi)存分配代碼使用系統(tǒng)號(hào),系統(tǒng)中的CPU和內(nèi)存塊都有對(duì)應(yīng)的系統(tǒng)號(hào)。應(yīng)用程序使用的CPU號(hào)和內(nèi)存塊號(hào)是應(yīng)用號(hào),它用于指定在cpumemmap中CPU和內(nèi)存的親和關(guān)系。每個(gè)進(jìn)程、每個(gè)虛擬內(nèi)存區(qū)和Linux內(nèi)核都有cpumemmap,這些映射是在fork()、exec()調(diào)用或創(chuàng)建虛擬內(nèi)存區(qū)時(shí)繼承下來的,具有root權(quán)限的進(jìn)程可以擴(kuò)展cpumemmap,包括增加系統(tǒng)CPU和內(nèi)存塊。映射的修改將導(dǎo)致內(nèi)核調(diào)度代碼開始運(yùn)用新的系統(tǒng)CPU,存儲(chǔ)分配代碼使用新的內(nèi)存塊分配內(nèi)存頁,而已在舊塊上分配的內(nèi)存則不能遷移。Cpumemmap中不允許有空洞,例如,假設(shè)cpumemmap的大小為n,則映射的應(yīng)用號(hào)必須從0到n-1。
Cpumemmap中系統(tǒng)號(hào)和應(yīng)用號(hào)并不是一對(duì)一的映射,多個(gè)應(yīng)用號(hào)可以映射到同一個(gè)系統(tǒng)號(hào)。
4.2 cpumemset
系統(tǒng)啟動(dòng)時(shí),Linux內(nèi)核創(chuàng)建一個(gè)缺省的cpumemmap和cpumemset,在初始的cpumemmap映射和cpumemset中包含系統(tǒng)目前所有的CPU和內(nèi)存塊信息。
Linux內(nèi)核只在該任務(wù)cpumemset的CPU上調(diào)度該任務(wù),并只從該區(qū)域的內(nèi)存列表中選擇內(nèi)存區(qū)分配給用戶虛擬內(nèi)存區(qū),內(nèi)核則只從附加到正在執(zhí)行分配請(qǐng)求CPU的cpumemset內(nèi)存列表中分配內(nèi)存。
一個(gè)新創(chuàng)建的虛擬內(nèi)存區(qū)是從任務(wù)創(chuàng)建的當(dāng)前cpumemset獲得的,如果附加到一個(gè)已存在的虛擬內(nèi)存區(qū)時(shí),情況會(huì)復(fù)雜些,如內(nèi)存映射對(duì)象和Unix System V的共享內(nèi)存區(qū)可附加到多個(gè)進(jìn)程,也可以多次附加到同一個(gè)進(jìn)程的不同地方。如果被附加到一個(gè)已存在的內(nèi)存區(qū),缺省情況下新的虛擬內(nèi)存區(qū)繼承當(dāng)前附加進(jìn)程的cpumemset,如果此時(shí)標(biāo)志位為CMS_SHARE,則新的虛擬內(nèi)存區(qū)鏈接到同一個(gè)cpumemset。
當(dāng)分配頁時(shí),如果該任務(wù)運(yùn)行的CPU在cpumemset中有對(duì)應(yīng)的存儲(chǔ)區(qū),則內(nèi)核從該CPU的內(nèi)存列表中選擇,否則從缺省的CPU對(duì)應(yīng)的cpumemset選擇內(nèi)存列表。
4.3硬分區(qū)和CpuMemSets
在一個(gè)大的NUMA系統(tǒng)中,用戶往往希望控制一部分CPU和內(nèi)存給某些特殊的應(yīng)用。目前主要有兩種技術(shù)途徑:硬分區(qū)和軟分區(qū)技術(shù),CpuMemSets是屬于軟分區(qū)技術(shù)。將一個(gè)大NUMA系統(tǒng)的硬分區(qū)技術(shù)與大NUMA系統(tǒng)具有的單系統(tǒng)映像優(yōu)勢(shì)是矛盾的,而CpuMemSets允許用戶更加靈活的控制,它可以重疊、劃分系統(tǒng)的CPU和內(nèi)存,允許多個(gè)進(jìn)程將系統(tǒng)看成一個(gè)單系統(tǒng)映像,并且不需要重啟系統(tǒng),保障某些CPU和內(nèi)存資源在不同的時(shí)間分配給指定的應(yīng)用。
SGI的CpuMemSets軟分區(qū)技術(shù)有效解決硬分區(qū)中的不足,一個(gè)單系統(tǒng)的SGI ProPack Linux服務(wù)器可以分成多個(gè)不同的系統(tǒng),每個(gè)系統(tǒng)可以有自己的控制臺(tái)、根文件系統(tǒng)和IP網(wǎng)絡(luò)地址。每個(gè)軟件定義的CPU組可以看成一個(gè)分區(qū),每個(gè)分區(qū)可以重啟、安裝軟件、關(guān)機(jī)和更新軟件。分區(qū)間通過SGI NUMAlink連接進(jìn)行通訊,分區(qū)間的全局共享內(nèi)存由XPC和XPMEM內(nèi)核模塊支持,它允許一個(gè)分區(qū)的進(jìn)程訪問另一個(gè)分區(qū)的物理內(nèi)存。
五、測(cè)試
為了有效驗(yàn)證Linux NUMA系統(tǒng)的性能和效率,我們?cè)赟GI公司上海辦事處測(cè)試了NUMA架構(gòu)對(duì)SGI Altix 350性能。
該系統(tǒng)的配置如下:CPU:8個(gè)1.5 GHz Itanium2 內(nèi)存:8GB 互連結(jié)構(gòu):如圖3所示
圖3 SGI Altix350 4個(gè)計(jì)算模塊的Ring拓?fù)?/p>
測(cè)試用例:
1、Presta MPI測(cè)試包(來自ASCI Purple的Benchmark)
從互連拓?fù)浣Y(jié)構(gòu)可以看出,計(jì)算模塊內(nèi)部的訪存延遲不需要通過互連,延遲最逗,剩下的需要通過1步或2步互連到達(dá)計(jì)算模塊,我們通過Presta MPI測(cè)試包,重點(diǎn)測(cè)試每步互連對(duì)系統(tǒng)的影響,具體結(jié)果如下:
最小延遲(us) | 一步延遲(us) | 兩步延遲(us) |
---|---|---|
1.6 | 1.8 | 2.0 |
2、NASA的NPB測(cè)試
上述測(cè)試表明,SGI Altix 350系統(tǒng)具有較高的訪存和計(jì)算性能,Linux NUMA技術(shù)已進(jìn)入實(shí)用階段。
-
內(nèi)存
+關(guān)注
關(guān)注
8文章
2975瀏覽量
73815 -
硬件
+關(guān)注
關(guān)注
11文章
3212瀏覽量
66064 -
機(jī)器
+關(guān)注
關(guān)注
0文章
777瀏覽量
40667 -
存儲(chǔ)管理
+關(guān)注
關(guān)注
0文章
31瀏覽量
9161 -
numa
+關(guān)注
關(guān)注
0文章
7瀏覽量
3829
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論