頁描述符
我們都知道Linux的內存是分頁的,在Linux 中每頁的大小是4KB(大部分情況下),Linux需要記錄下來每一頁的狀態,于是很顯然地,需要一些額外的內存去存儲這些狀態信息,對于每一頁內存,linux 需要用32個字節去管理(可能與最新版的linux有所出入),稱之為頁描述符,這些頁描述符加起來大概占用不到整個內存的1%。
頁描述符具體都干了什么?
從最初的Linux版本發展到現在,這個頁描述符已經變得相當復雜,下面是一個簡化版本
129 typedef struct pglist_data {
130? ? zone_t node_zones[MAX_NR_ZONES];
131? ? zonelist_t node_zonelists[GFP_ZONEMASK+1];
132? ? int nr_zones;
133? ? struct page *node_mem_map;
134? ? unsigned long *valid_addr_bitmap;
135? ? struct bootmem_data *bdata;
136? ? unsigned long node_start_paddr;
137? ? unsigned long node_start_mapnr;
138? ? unsigned long node_size;
139? ? int node_id;
140? ? struct pglist_data *node_next;
141 } pg_data_t;
這里面最重要的兩個字段是
count:
代表該頁的引用計數器,如果等于-1,則代表相應的頁框是空閑,可以被分配給任意一個進程或者內核本身,如果大于0則代表已經被分配給了一個或者多個進程
flags:
描述該內存頁的狀態,標志說明
PG_locked頁被鎖定
PG_error在傳輸過程中發生I/O錯誤
PG_referenced剛剛訪問過的頁
PG_uptodate在完成讀操作后置位
等等..
內存管理區
在實際的計算機體系結構中,Linux并不能對所有的內存一視同仁,因為現有的硬件約束不允許,主要來自以下兩點
1、ISA總線的DMA直接內存存取處理器有嚴格的限制,只能對RAM的前16M尋址(因為ISA的總線只有16位)
2、在具有大容量RAM的現代32位計算機中,CPU不能 訪問所有的物理內存,因為線性地址太小?
為了應對這兩種限制,Linux把每個內存節點的物理內存劃分為3個管理區
1、ZONE_DMA,包含低于16M的內存頁框
2、ZONE_NORMAL,包含低于16M且低于896M的內存頁框
3、ZONE_HIGHMEM,包含高于896M的內存頁框
想必有一個問題是無法避免的,為什么ZONE_NORMAL只包含到896M的內存?
我的個人理解是,Linux把地址空間分為用戶地址空間和內核地址空間,在4GB的LInux機器上3GB用來做用戶地址空間,1GB用來做內核地址空間(大部分情況下是這樣,可以配置),內核有時需要訪問用戶地址空間中的物理地址,于是內核劃分128M來用于映射用戶地址空間的地址,如此一來1GB-128M=896MB。
這種情況在64位系統上有所不同,在64位系統上,LInux也只使用其中的48位用來尋址,也就是說, 總的虛擬地址空間為256TB( 2^48 )。這其中0000000000000000 - 00007fffffffffff(128TB)為用戶空間, ffff800000000000 - ffffffffffffffff(128TB)為內核空間,可以看到內核空間之大,已經可以足夠映射所有的物理內存了,所以在X86-64的Linux系統上,ZONE_HIGHMEM不存在。
還有一個名詞叫做內存節點,這個東西很不常用,主要是有一種比較詭異的內存模型稱之為Non-Uniform Memory Access (NUMA),在這種內存模型下,CPU對不同地址尋址的性能可能會不盡相同,LInux為了支持該內存模型,提出了內存節點的概念,把性能相近的地址范圍劃分在一個內存節點中。整個內存的數據結構如下圖所示:
pg_data_t其實就是一個內存節點,在NUMA的機器上可能會有多個,在UMA(Uniform Memory Access)的機器上只有一個,NUMA至少我是沒見過。
node_zones其實指的就是內存節點中的物理內存劃分了,也就是我們上面講的那些。
struct page就是頁描述符
如有錯誤請指正。
參考:
《深入理解LInux內核》第三版
https://www.kernel.org/doc/gorman/html/understand/understand005.html