頁面目錄PGD, 中間目錄PMD 和 頁面表PT 分別是由 pgd_t, pmd_t, pte_t 構成的數組, 下面給出他們的定義:
==================== include/asm-i386/page.h 36 50 ====================
36 /*
37 * These are used to make use of C type-checking..
38 */
39 #if CONFIG_X86_PAE
40 typedef struct { unsigned long pte_low, pte_high; } pte_t;
41 typedef struct { unsigned long long pmd; } pmd_t;
42 typedef struct { unsigned long long pgd; } pgd_t;
43 #define pte_val(x) ((x).pte_low | ((unsigned long long)(x).pte_high << 32))
44 #else
45 typedef struct { unsigned long pte_low; } pte_t;
46 typedef struct { unsigned long pmd; } pmd_t;
47 typedef struct { unsigned long pgd; } pgd_t;
48 #define pte_val(x) ((x).pte_low)
49 #endif
50 #define PTE_MASK PAGE_MASK
根據定義, 我們知道pgd_t, pmd_t, pte_t 實際上就是長整數。
我們知道, 物理頁面都是跟4K 字節的邊界對齊的, 因而, 物理頁面起始的高 20 bit 可以看成是物理頁面的序號, 余下的 低 12 bit 可以用來表征頁面的狀態信息和訪問權限, 就像 PGD 中所做的那樣。然而, 內核中並沒有在 pte_t 中定義有關的位段, 而是在page.h 中另行定義了一個用來說明頁面保護的結構 pgprot_t
typedef struct { unsigned long pgprot; } pgprot_t;
這個結構的值與i386 MMU 的頁面表項的低12bit 相對應, 表征所映射頁面的當前的狀態和訪問權限。
實際操作中,pgprot 數值小於 0x1000, 而pte 中數值大於 0x1000, 通過 __mk_pte 宏 可以得到實際用於頁面表中的表項:
==================== include/asm-i386/pgtable-2level.h 61 61 ====================
61 #define __mk_pte(page_nr,pgprot) __pte(((page_nr) << PAGE_SHIFT) | pgprot_val(pgprot))
==================== include/asm-i386/page.h 56 58 ====================
56 #define pgprot_val(x) ((x).pgprot)
58 #define __pte(x) ((pte_t) { (x) } )
1.2 mem_map
在內核中有一個全局的指針 mem_map, 他指向一個page 數據結構的數組。而每個page 結構代表了一個物理頁面, 整個的這個 page 數組代表了系統中的全部的物理頁面。
也就是說, 頁面表項的高20 bit 對應了一個物理頁面的編號, 通過這個編號, 我們可以在這個mem_map 所對應的page 數組中找到相應的 代表這個物理頁面的 page 數據結構, 而通過在這個高 20 bit 數據後面添加 12 個 0 之後, 就可以得到物理頁面的起始地址了。
1.3 pgprot 中的P標志位
在映射過程中, MMU 首先檢查的是 P 標志位, (就是表項中最低位), 他標志著 所映射的物理頁面是否在內存中。只有 P 為 1, 才完成映射, 否則會產生缺頁異常。
1.4 pte_page, virt_to_page
內核中使用 pte_page 從頁面表項獲取物理頁面結構地址,
用 virt_to_page 從虛擬地址找到相應物理頁面的page 結構
==================== include/asm-i386/pgtable-2level.h 59 59 ====================
59 #define pte_page(x) (mem_map+((unsigned long)(((x).pte_low >> PAGE_SHIFT))))
==================== include/asm-i386/page.h 117 117 ====================
117 #define virt_to_page(kaddr) (mem_map + (__pa(kaddr) >> PAGE_SHIFT))
1.5 物理頁面的page 結構(mem_map_t)
==================== include/linux/mm.h 126 148 ====================
126 /*
127 * Try to keep the most commonly accessed fields in single cache lines
128 * here (16 bytes or greater). This ordering should be particularly
129 * beneficial on 32-bit processors.
130 *
131 * The first line is data used in page cache lookup, the second line
132 * is used for linear searches (eg. clock algorithm scans).
133 */
134 typedef struct page {
135 struct list_head list;
136 struct address_space *mapping;
137 unsigned long index;
138 struct page *next_hash;
139 atomic_t count;
140 unsigned long flags; /* atomic flags, some possibly updated asynchronously */
141 struct list_head lru;
142 unsigned long age;
143 wait_queue_head_t wait;
144 struct page **pprev_hash;
145 struct buffer_head * buffers;
146 void *virtual; /* non-NULL if kmapped */
147 struct zone_struct *zone;
148 } mem_map_t;
內核中通常使用 page 或者 map 來表示這個數據結構。
ps: 這個結構中的各個成分的次序是有講究的, 目的是使得聯系緊密的若干成分, 在執行被裝入高速緩存的同一緩沖線中(16 字節)。
index 表明頁面在文件中的序號, 或者去向。
上面提到了, mem_map 是內核中指向一個page結構的數組, 相當於一個物理頁面的倉庫。在系統初始化的時候, 被建立起來。 而這個倉庫主要被劃分為兩個部分: ZONE_DMA 和 ZONE_NORMAL 。
1.6 ZONE_DMA
ZONE_DMA 管理區內的頁面 主要是提供給 DMA 使用的。由於 DMA 交換不通過CPU 需要確保有一定的空間,以及有些外設的特殊要求, 或者 當DMA 所需要的緩沖區大小超過一個物理頁面大小的時候, 要求這兩個頁面在物理上連續, (這是無法通過MMU 來保證的)。 基於這些原因, DMA 所用的物理地址需要單獨劃分一個區域。
1.7 管理區數據結構 zone_struct
==================== include/linux/mmzone.h 11 58 ====================
11 /*
12 * Free memory management - zoned buddy allocator.
13 */
14
15 #define MAX_ORDER 10
16
17 typedef struct free_area_struct {
18 struct list_head free_list;
19 unsigned int *map;
20 } free_area_t;
21
22 struct pglist_data;
23
24 typedef struct zone_struct {
25 /*
26 * Commonly accessed fields:
27 */
28 spinlock_t lock;
29 unsigned long offset;
30 unsigned long free_pages;
31 unsigned long inactive_clean_pages;
32 unsigned long inactive_dirty_pages;
33 unsigned long pages_min, pages_low, pages_high;
34
35 /*
36 * free areas of different sizes
37 */
38 struct list_head inactive_clean_list;
39 free_area_t free_area[MAX_ORDER];
40
41 /*
42 * rarely used fields:
43 */
44 char *name;
45 unsigned long size;
46 /*
47 * Discontig memory support fields.
48 */
49 struct pglist_data *zone_pgdat;
50 unsigned long zone_start_paddr;
51 unsigned long zone_start_mapnr;
52 struct page *zone_mem_map;
53 } zone_t;
54
55 #define ZONE_DMA 0
56 #define ZONE_NORMAL 1
57 #define ZONE_HIGHMEM 2
58 #define MAX_NR_ZONES 3
在這個管理區結構 zone_t 中 存在一組空閒區間隊列 free_area_t, 這是因為, 我們常常需要按塊來分配物理空間中的連續的頁面。 於是有了 1, 2, 4, 8 , 16 。。。。等大小的塊結構。
offset 表明分區在mem_map 中的起始頁面編號。
1.8 NUMA
傳統計算機結構中, 整個物理空間都是均勻一致的, cpu 訪問這個空間中的任何一個地址所需要的時間都是相同的, 我們將它稱為是 UMA (均質存儲結構)。
然而, 這種情況是理想的,現實中一般都是 NUMA 結構。
為了支持 NUMA 結構, 管理區不在作為最高的機構,
他設置了多個存儲節點, 對每個存儲節點, 采用類似 UMA 時候的管理方式, ie, 在管理區結構 zone_struct 以及 page 結構數組的上方 多了一層代表著存儲節點的 pglist_data 數據結構。
==================== include/linux/mmzone.h 79 90 ====================
79 typedef struct pglist_data {
80 zone_t node_zones[MAX_NR_ZONES];
81 zonelist_t node_zonelists[NR_GFPINDEX];
82 struct page *node_mem_map;
83 unsigned long *valid_addr_bitmap;
84 struct bootmem_data *bdata;
85 unsigned long node_start_paddr;
86 unsigned long node_start_mapnr;
87 unsigned long node_size;
88 int node_id;
89 struct pglist_data *node_next;
90 } pg_data_t;
若干存儲節點的pglist_data數據結構通過指針 node_next 形成了一個單鏈隊列。
node_zones 表征節點的管理區, node_mem_map 表征指向 page 的結構數組。
相應的 zone_t 結構中也有一個指針 zone_pgdat 指向所屬節點的pglist_data 結構。
==================== include/linux/mmzone.h 71 74 ====================
71 typedef struct zonelist_struct {
72 zone_t * zones [MAX_NR_ZONES+1]; // NULL delimited
73 int gfp_mask;
74 } zonelist_t;
這個 zonelist_t 結構用 zones 數組 來表征不同頁面的分配策略。
1.9 小結
物理空間的管理方面, 頂層的是 存儲節點, 下方是管理區 和 page 數組。 我們可以這麼去理解, 原先的UMA 結構在現在的模型中僅僅只是 NUMA 結構下方的一個存儲節點而已。
我們拿到一個物理地址, 先得到他所在的存儲節點位置 pglist_data, 繼而 從這個結構中可以得到node_mem_map 就是我們所需要的 page 數組了, 通過 pte 表項的前 20 bit 可以定位得到這個 page 了。
2. 虛擬空間管理
2.1 虛擬空間的特殊性
虛擬空間管理和物理空間不同, 他沒有一個總的物理頁面的大倉庫, 而是以進程為基礎, 每個進程都有各自的虛擬存儲空間(用戶空間)。
物理空間管理, 我們主要是從 供 的角度管理, 而虛擬空間的管理, 更多的是從需求的角度來切入了。
2.2 vm_area_struct
這是一個對虛存區間抽象的一個重要的數據結構, 每一個離散的虛存空間都有一個 vma 與之相對應:
==================== include/linux/mm.h 35 69 ====================
35 /*
36 * This struct defines a memory VMM memory area. There is one of these
37 * per VM-area/task. A VM area is any part of the process virtual memory
38 * space that has a special rule for the page-fault handlers (ie a shared
39 * library, the executable area etc).
40 */
41 struct vm_area_struct {
42 struct mm_struct * vm_mm; /* VM area parameters */
43 unsigned long vm_start;
44 unsigned long vm_end;
45
46 /* linked list of VM areas per task, sorted by address */
47 struct vm_area_struct *vm_next;
48
49 pgprot_t vm_page_prot;
50 unsigned long vm_flags;
51
52 /* AVL tree of VM areas per task, sorted by address */
53 short vm_avl_height;
54 struct vm_area_struct * vm_avl_left;
55 struct vm_area_struct * vm_avl_right;
56
57 /* For areas with an address space and backing store,
58 * one of the address_space->i_mmap{,shared} lists,
59 * for shm areas, the list of attaches, otherwise unused.
60 */
61 struct vm_area_struct *vm_next_share;
62 struct vm_area_struct **vm_pprev_share;
63
64 struct vm_operations_struct * vm_ops;
65 unsigned long vm_pgoff; /* offset in PAGE_SIZE units, *not* PAGE_CACHE_SIZE */
66 struct file * vm_file;
67 unsigned long vm_raend;
68 void * vm_private_data; /* was vm_pte (shared mem) */
69 };
內核中這個結構的變量名通常都是 vma
vm_start 和 vm_end 標記了一個虛存空間, vm_page_prot 和 vm_flags 表征這個虛存空間的權限等信息, 一個區間內的所有頁面都應該有相同的訪問權限和保護屬性。
利用 vm_next 指針將同一個進程空間內所有的虛存地址的高低次序連接在一起。由於, 通常涉及到給定一個虛擬地址, 需要找出他所在的區間的操作, 如果僅僅只是鏈表的話, 效率不高, 於是這裡還引入了AVL 樹。
vm_avl_height, vm_avl_left, vm_avl_right 的三個成分就是用於 AVL 樹, 表示本區間在 AVL 樹中的相應位置的。
使用 vm_next_share, vm_pprev_share, vm_file 表征虛存空間與磁盤文件之間的關聯。
vm_ops 指向一個 vm_operation_struct 數據結構的指針
==================== include/linux/mm.h 115 124 ====================
115 /*
116 * These are the virtual MM functions - opening of an area, closing and
117 * unmapping it (needed to keep files on disk up-to-date etc), pointer
118 * to the functions called when a no-page or a wp-page exception occurs.
119 */
120 struct vm_operations_struct {
121 void (*open)(struct vm_area_struct * area);
122 void (*close)(struct vm_area_struct * area);
123 struct page * (*nopage)(struct vm_area_struct * area, unsigned long address, int write_access);
124 };
這個結構由3個函數指針構成, 分別用於虛存空間的打開, 關閉 和 建立映射。
2.3 vm_mm
==================== include/linux/sched.h 203 227 ====================
203 struct mm_struct {
204 struct vm_area_struct * mmap; /* list of VMAs */
205 struct vm_area_struct * mmap_avl; /* tree of VMAs */
206 struct vm_area_struct * mmap_cache; /* last find_vma result */
207 pgd_t * pgd;
208 atomic_t mm_users; /* How many users with user space? */
209 atomic_t mm_count; /* How many references to "struct mm_struct" (users count as 1) */
210 int map_count; /* number of VMAs */
211 struct semaphore mmap_sem;
212 spinlock_t page_table_lock;
213
214 struct list_head mmlist; /* List of all active mm's */
215
216 unsigned long start_code, end_code, start_data, end_data;
217 unsigned long start_brk, brk, start_stack;
218 unsigned long arg_start, arg_end, env_start, env_end;
219 unsigned long rss, total_vm, locked_vm;
220 unsigned long def_flags;
221 unsigned long cpu_vm_mask;
222 unsigned long swap_cnt; /* number of pages to swap on next pass */
223 unsigned long swap_address;
224
225 /* Architecture-specific MM context */
226 mm_context_t context;
227 };
這是一個比vm_area_t 更高層次上使用的數據結構。每個進程只有一個 mm_struct 結構。, 在每個進程的進程 控制塊, task_struct 結構中, 有一個指針指向該進程的 mm_struct 結構。
一個進程只有一個 mm_struct, 但是 一個 mm_struct 可以被多個進程共享, ex. vfork(), clone()
2.4 小結
ps:
mm_struct 結構和他下屬的各個 vm_area_struct 只是表明了他對虛存空間的需求, page , zone_struct 等結構則說明了對頁面的供應, 而 PGD, PMD, PT 等則是他們兩者之間的橋梁