linux源代码解读,【原创】Linux MM 源代码解读 (1)
最近轉載了不少文章,自己也讀了ULK3 MM章節數遍,小有體會。寫一些心得,以免自己忘記。
之前一些virtual address到physical address的映射的基本MM機制,可以參看博客轉載的其他文章,不再一一贅敘。本文打算主要focus在源碼和一些細節的理解上。另外提一句,我看的kernel版本是2.4.0
在描述之前要對兩個看起來很簡單的基本概念進行澄清,其實不是看起來那么簡單。page and page frame。
根據ULK3 P.50描述,線性地址被分成以固定長度為單位的組,稱為page。說明page是一個線性地址,也就是Linux中的虛擬地址的概念。而且page既指一組線性地址(4KB),又包括在這組地址中的數據。(我目前的理解)但是在函數alloc_pages()中的返回值struct page*為一個物理地址的指針。我們可以用void* page_address(struct page *page)將其轉化為邏輯地址。(這個意義上說page又是指物理地址...)
而page frame是指分頁單元把所有RAM分成固定長度的frame,每個page frame包括一個page。這是一個physical memory的概念。
現在我們考慮NUMA。Linux 把物理內存劃分為3個層次來管理:存儲節點(Node)、管理區(Zone)和頁面(Page),并用3 個相應的數據結構來描述。為什么這樣劃分,后面會講到。
先看看整體的結構:
all_mem
/ ? ? | ? ? ? \
/ ? | ? ? ? ?\
pglist_data ? pglist_data ?pglist_data
/ ?| ? \
/ ? ? ?| ? ? ? \
zone_dma zone_normal zone_high
|
page
|
page
整個memory分成不同的pglist_data,每個pglist_data代表一個Node,每個Node又分為三個zone,分別是ZONE_DMA, ZONE_NORMAL, ZONE_HIGHMEM。每個zone里面由若干個page組成。
我們結合代碼看一下這幾個結構聲明。
1)Page
先說說我們比較熟悉的page,對一個page的描述在文件 include/linux/mm.h struct page中。貼過來看一下。
typedef struct page {
struct list_head list;
struct address_space *mapping;
unsigned long index;
struct page *next_hash;
atomic_t count;
unsigned long flags;/* atomic flags, some possibly updated asynchronously */
struct list_head lru;
unsigned long age;
wait_queue_head_t wait;
struct page **pprev_hash;
struct buffer_head * buffers;
void *virtual; /* non-NULL if kmapped */
struct zone_struct *zone;
} mem_map_t;
系統在每個物理頁面都有一個page結構,根據陳莉君教授在kernel寶典中的描述,初始化時候會根據內存大小建立一個page結構的數組mem_page。
2) Node
這個地方就和NUMA有關系了。因為不同的內存經過不同的總線被訪問到,或者一些類似的原因,使得CPU對不同內存單元的訪問時間不一樣。因此Kernel把所有物理內存分為幾個Node,每個Node中,可以理解成是由同一種介質的內存組成,所以相同CPU訪問頁面所需要的時間相同。(我個人猜想如果有其他的內存插在另外一臺通過系統總線連接的服務器上,這樣Kernel就會相應分成兩個Node的意思吧。)一般我們在一臺PC機上不需要考慮多個Node的問題,都是UMA,也就是只有一個Node。
Node的數據結構為pglist_data,描述于include/linux/mmzone.h 中:
typedef struct pglist_data {
zone_t node_zones[MAX_NR_ZONES];
zonelist_t node_zonelists[NR_GFPINDEX];
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;
extern int numnodes;
extern pg_data_t *pgdat_list;
若干存儲節點的pglist_data 數據結構可以通過node_next 形成一個單鏈表隊列。
每個結構中的node_mem_map 指向具體節點的page 結構數組,而數組node_zone[]就是該節點的最多3 個頁面管理區。
3) Zone
Linux 又把物理頁面劃分為3個區:
? 專供DMA 使用的ZONE_DMA 區(小于16MB);
? 常規的ZONE_NORMAL 區(大于16MB 小于896MB);
? 內核不能直接映射的區ZONE_HIGMEM 區(大于896MB)。
每個zone都用struct zone_struct 結構來表示, 描述于include/linux/mmzone.h:
typedef struct zone_struct {
/*
* Commonly accessed fields:
*/
spinlock_tlock;
unsigned longoffset;
unsigned longfree_pages;
unsigned longinactive_clean_pages;
unsigned longinactive_dirty_pages;
unsigned longpages_min, pages_low, pages_high;
/*
* free areas of different sizes
*/
struct list_headinactive_clean_list;
free_area_tfree_area[MAX_ORDER];
/*
* rarely used fields:
*/
char*name;
unsigned longsize;
/*
* Discontig memory support fields.
*/
struct pglist_data*zone_pgdat;
unsigned longzone_start_paddr;
unsigned longzone_start_mapnr;
struct page*zone_mem_map;
} zone_t;
對struct zone_struct結構中每個域的描述如下:
lock :用來保證對該結構中其它域的串行訪問
free_pages :在這個區中現有空閑頁的個數
pages_min、pages_low及 pages_high是對這個區最少、此少及最多頁面個數的描述
need_balance:與kswapd合在一起使用
free_area:在伙伴分配系統中的位圖數組和頁面鏈表
zone_pgdat:本管理區所在的存儲節點
zone_mem_map:該管理區的內存映射表
zone_start_paddr:該管理區的起始物理地址
zone_start_mapnr:在mem_map中的索引(或下標)
name:該管理區的名字
size:該管理區物理內存總的大小
其中,free_area_t定義為:
#difine ? MAX_ORDER ?10
type struct free_area_struct {
struct list_head ? free_list
unsigned ?int ? ?*map
} free_area_t
zone_struct結構中的free_area[MAX_ORDER]是一組“空閑區間”鏈表。為什么要定義一組而不是一個空閑隊列呢?這是因為常常需要成塊地在物理空間分配連續的多個頁面,所以要按塊的大小分別加以管理。因此,在管理區數據結構中既要有一個隊列來保持一些離散(連續長度為1)的物理頁面,還要有一個隊列來保持一些連續長度為2的頁面塊以及連續長度為4、8、16、…、直至2^MAX_ORDER(即4M字節)的隊列。
總結
以上是生活随笔為你收集整理的linux源代码解读,【原创】Linux MM 源代码解读 (1)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux6.3支持gcc版本,Linu
- 下一篇: linux加密格式化吗,linux环境下