SLIT 表則記錄了各個結(jié)點之間的距離,在系統(tǒng)中由數(shù)組 node_distance[ ] 記錄。


    Linux 采用 Node、Zone 和頁三級結(jié)構(gòu)來描述物理內(nèi)存的,如圖 2 所示, 
  
  
  
      圖 2 Linux 中 Node、Zone 和頁的關(guān)系
  
  . 1 結(jié)點
  
    Linux 用一個 struct pg_data_t 結(jié)構(gòu)來描述系統(tǒng)的內(nèi)存,系統(tǒng)中每個結(jié)點都掛接在一個 pgdat_list 列表中,對 UMA 體系結(jié)構(gòu),則只有一個靜態(tài)的 pg_data_t 結(jié)構(gòu) contig_page_data。對 NUMA 系統(tǒng)來說則非常容易擴充,NUMA 系統(tǒng)中一個結(jié)點可以對應 Linux 存儲描述中的一個結(jié)點,具體描述見 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)中的主要域進行說明: 
































說明
Node_zones 該結(jié)點的 zone 類型,一般包括 ZONE_HIGHMEM、ZONE_NORMAL 和 ZONE_DMA 三類
Node_zonelists 分配時內(nèi)存時 zone 的排序。它是由 free_area_init_core() 通過 page_alloc.c 中的 build_zonelists() 設置 zone 的順序
nr_zones 該結(jié)點的 zone 個數(shù),可以從 1 到 3,但并不是所有的結(jié)點都需要有 3 個 zone
node_mem_map 它是 struct page 數(shù)組的第一頁,該數(shù)組表示結(jié)點中的每個物理頁框。根據(jù)該結(jié)點在系統(tǒng)中的順序,它可在全局 mem_map 數(shù)組中的某個位置
Valid_addr_bitmap 用于描述結(jié)點內(nèi)存空洞的位圖
node_start_paddr 該結(jié)點的起始物理地址
node_start_mapnr 給出在全局 mem_map 中的頁偏移,在free_area_init_core() 計算在 mem_map 和 lmem_map 之間的該結(jié)點的頁框數(shù)目
node_size 該 zone 內(nèi)的頁框總數(shù)
node_id 該結(jié)點的 ID,全系統(tǒng)結(jié)點 ID 從 0 開始

    系統(tǒng)中所有結(jié)點都維護在 pgdat_list 列表中,在 init_bootmem_core 函數(shù)中完成該列表初始化工作。 
  
     2.2 Zone
  
    每個結(jié)點的內(nèi)存被分為多個塊,稱為zones,它表示內(nèi)存中一段區(qū)域。一個zone用struct_zone_t結(jié)構(gòu)描述,zone的類型主要有ZONE_DMA、ZONE_NORMAL和ZONE_HIGHMEM。ZONE_DMA位于低端的內(nèi)存空間,用于某些舊的ISA設備。ZONE_NORMAL的內(nèi)存直接映射到Linux內(nèi)核線性地址空間的高端部分,許多內(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;



      下面就該結(jié)構(gòu)中的主要域進行說明: 






































說明
Lock 旋轉(zhuǎn)鎖,用于保護該zone
free_pages 該zone空閑頁總數(shù)
pages_min,

pages_low,


pages_high

Zone的閾值
need_balance 該標志告訴kswapd需要對該zone的頁進行交換
Free_area 空閑區(qū)域的位圖,用于buddy分配器
wait_table 等待釋放該頁進程的隊列散列表,這對wait_on_page()和unlock_page()是非常重要的。當進程都在一條隊列上等待時,將引起進程的抖動
zone_mem_map 全局mem_map中該zone所引用的第一頁
zone_start_paddr 含義與node_start_paddr類似
zone_start_mapnr 含義與node_start_mapnr類似
Name 該zone的名字。如,“DMA”,“Normal”或“HighMem”
Size Zone的大小,以頁為單位

    當系統(tǒng)中可用的內(nèi)存比較少時,kswapd將被喚醒,并進行頁交換。如果需要內(nèi)存的壓力非常大,進程將同步釋放內(nèi)存。如前面所述,每個zone有三個閾值,稱為pages_low,pages_min和pages_high,用于跟蹤該zone的內(nèi)存壓力。pages_min的頁框數(shù)是由內(nèi)存初始化free_area_init_core函數(shù),根據(jù)該zone內(nèi)頁框的比例計算的,最小值為20頁,最大值一般為255頁。當?shù)竭_pages_min時,分配器將采用同步方式進行kswapd的工作;當空閑頁的數(shù)目達到pages_low時,kswapd被buddy分配器喚醒,開始釋放頁;當達到pages_high時,kswapd將被喚醒,此時kswapd不會考慮如何平衡該zone,直到有pages_high空閑頁為止。一般情況下,pages_high缺省值是pages_min的3倍。
  
    Linux存儲管理的這種層次式結(jié)構(gòu)可以將ACPI的SRAT和SLIT信息與Node、Zone實現(xiàn)有效的映射,從而克服了傳統(tǒng)Linux中平坦式結(jié)構(gòu)無法反映NUMA架構(gòu)的缺點。當一個任務請求分配內(nèi)存時,Linux采用局部結(jié)點分配策略,首先在自己的結(jié)點內(nèi)尋找空閑頁;如果沒有,則到相鄰的結(jié)點中尋找空閑頁;如果還沒有,則到遠程結(jié)點中尋找空閑頁,從而在操作系統(tǒng)級優(yōu)化了訪存性能。