歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> 關於Linux >> Linux 內核源代碼情景分析 chap2 存儲管理(二)

Linux 內核源代碼情景分析 chap2 存儲管理(二)

日期:2017/3/1 12:14:38   编辑:關於Linux

幾個重要的數據結構和函數

1. 物理地址管理

1.1 pgd_t, pmd_t, pte_t

頁面目錄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 等則是他們兩者之間的橋梁
這裡寫圖片描述

Copyright © Linux教程網 All Rights Reserved