linux內存管理之數據結構

linux內存管理之數據結構


1、物理空間管理

1.1 頁表項

[include /asm-i386/page.h: 39]node

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

當中pte_t爲頁表項,在i386當中。一個頁的大小爲4K。這意味着也表項的低12位是0,高20位是物理頁的起始地址。linux

因此低12位可以用來保存頁面保護和訪問權限信息。頁面保護和訪問權限信息被定義在pgprot_t中
[include/asm-i386/page.h : 52]git

52 typedef struct { unsigned long pgprot; } pgprot_t;

pgprot_t的低12位定義例如如下:
[include/asm-i386/pgtable.h : 152]github

152 #define _PAGE_BIT_PRESENT 0
153 #define _PAGE_BIT_RW 1
154 #define _PAGE_BIT_USER 2
155 #define _PAGE_BIT_PWT 3
156 #define _PAGE_BIT_PCD 4
157 #define _PAGE_BIT_ACCESSED 5
158 #define _PAGE_BIT_DIRTY 6
159 #define _PAGE_BIT_PSE 7 /* 4 MB (or 2MB) page, Pentium+, if present.. */
160 #define _PAGE_BIT_GLOBAL 8 /* Global TLB entry PPro+ */
161     
162 #define _PAGE_PRESENT 0x001
163 #define _PAGE_RW 0x002
164 #define _PAGE_USER 0x004
165 #define _PAGE_PWT 0x008
166 #define _PAGE_PCD 0x010
167 #define _PAGE_ACCESSED 0x020
168 #define _PAGE_DIRTY 0x040
169 #define _PAGE_PSE 0x080 /* 4 MB (or 2MB) page, Pentium+, if present.. */
170 #define _PAGE_GLOBAL 0x100 /* Global TLB entry PPro+ */

將pgprot_t和pte_t的高20位合併就能夠獲得真正的頁表項,該操做有宏__make_pte來完畢
[include/asm-i386/pgtable-2level.h : 61]數組

61 #define __mk_pte(page_nr,pgprot) __pte(((page_nr) << PAGE_SHIFT) | pgprot_val(pgprot))

當MMU進行映射的時候,會首先檢查P位,也就是_PAGE_BIT_PRESENT(BIT0)位,假設該位爲1,則進行映射,不然就產生一個缺頁中斷。緩存

1.2 物理頁面管理對象page

內核維護一個全局的mem_page的page數組,每個page表明着一個物理頁面,整個數組內的page就表明了所有物理頁面。在內核定義中,可以用pte_t高20位當成索引去訪問mem_page數組,從而獲得該物理頁的page結構。page結構定義例如如下:
[include/linux/mm.h : 134]markdown

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;

假設頁面的內容來自一個文件,index表明該頁面在文件裏的序號。當頁面被交換出內存。但還保留內容做爲緩衝時,index指名了頁面的去向。
系統中的每個物理頁面都相應了一個page結構。也就是說一個page結構是物理頁面的「登記信息」,或者說「管理信息」。在系統初始化時。所有page結構被創建,並且page做爲一個物理頁面的管理結構,當一個page結構被分配出去的時候,就表示了一個物理頁面被分配使用。數據結構

2、內存分區

2.1 過去的分區

系統內存被分爲兩個區:app

  • ZONE_DMA
  • ZONE_NORMAL

(依據系統配置。還可以添加第三個區給ZONE_HIGHMEN,內核訪問超過1G的物理空間)
這意味着mem_page數組中的page也被相應分爲ZONE_DMA和ZONE_NORMAL兩組,而既然已經分組了,就會有分組管理信息,故而每個內存區域具備一個區域管理結構:zone_struct。定義例如如下:
[include/linux/mmzone.h : 24]async

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;

當中39行的free_area爲一組空暇區塊隊列,組內有「連續的空暇物理頁面」和「離散的空暇物理頁面」兩種隊列。因爲分配內存的時候有可能要求分配連續的物理頁,因此將連續的物理頁和離散的物理頁分開管理。而offset則表示該分區在mem_map中的起始號。

2.2 當下的分區狀況

但隨着NUMA*(非均質儲存結構)的引入,分區發生了變化。NUMA指的是,在當下多處理器結構中,每個CPU都具備本地儲存,稱之爲內存節點,而多個CPU之間又有共享的內存。

CPU訪問本地儲存的速度要快於訪問共享儲存的速度。也就是說,在一個連續的物理地址上,儲存器的訪問速度不一致,這就叫作NUMA。在NUMA結構中。假設要分配幾個連續的物理頁,通常要求分配在質地一樣的儲存器內。爲此,內核定義了一個pglist_data結構,每個pglist_data表明這一個內存節點。每個內存節點都可以擁有ZONE_DMA和ZONE_NORMAL(依據配置還可能有ZONE_HIGHMEN)兩個區,也就意味這每個內存節點都有一個page數組和一個zone_t數組,用於管理該節點上兩個區的所有page。總的來講。原來將整個物理空間分爲三個區來管理的模式,現在變成了將整個物理空間分爲若干內存節點。各個內存節點將節點上的所有物理空間進行分區管理*。pglist_data定義例如如下:
[include/linux/mmzone.h : 79]

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;

80: node_zones爲該內存節點上的三個分區管理結構。
81: node_zonelists爲一個指向zone_t指針數組鏈表(鏈表的每個節點上都掛着一個數組),鏈表上的每個節點包括一個指針數組。該數組的元素依照待定的次序指向每個儲存節點的node_zones的數組。前面說過。在NUMA中分配物理頁,每每要求在同一內存節點上分配,假設這時當前節點的空暇連續物理頁沒法知足分配,則可以經過這個指針數組依照0、一、二、三、4……的次序查找其餘儲存節點上的node_zones,直到找到一個可以知足分配的儲存節點。指針數組中的元素排列次序,就稱爲一種分配策略。比方說有儲存節點A、B、C、D、E。而node_zoneslist某節點上的指針數組元素排列次序爲pA、pC、pD、pB。這個策略就規定了:要分配物理頁的時候首先嚐試從A分配。假設A不能知足,就查找B,假設B不能知足就查找C….而假設該點的指針數組爲pC、pA、pD、pB。則這個策略規定:要分配物理頁的時候首先嚐試從C分配,假設C不能知足,就查找A。假設A不能知足就查找D….
因此把node_zonelists稱爲分配策略鏈表也不爲過。
82: node_mem_map數組包括了該內存節點上的所有page結構。

3、 虛擬空間管理

3.1 進程虛存區域

每個進程都擁有本身的3G進程空間和1G共享的內核空間。

很是少會有進程佔用到3G的空間,每每是佔用多個離的虛存區域。虛存區域的抽象數據結構定義例如如下:
[include/linux/mm.h : 41]

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 };

43: vm_start 該虛存區域的開始地址
44: vm_end 該虛存區域的結束地址
47: vm_next 用於將進程空間內所有的內存區域組成一個單項鍊表進行管理
49: pgprot_t 頁面保護權限,因爲一個內存區域中僅僅有一個用於描寫敘述頁面訪問權限pgprot_t這意味這在容許個區域中的所有頁面訪問權限是一致的。


54~55:

53     short vm_avl_height;
 54     struct vm_area_struct * vm_avl_left;
 55     struct vm_area_struct * vm_avl_right;

這三個成員用於將內存區域組成一棵AVL樹,因爲常常需要在進程空間中查找某個內存區域。假設用線性鏈表查找的方式,會影響效率。
61: vm_next_share ????
62 vm_pprev_share ???
64 vm_ops指向一個struct ,其實是一個跳轉表:

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 };

該跳轉表用於虛存區間的打開,關閉和創建,當中nopage在發生缺頁中斷的時候會被調用。

3.2 進程地址空間

在vm_area_struct結構中有一個成員vm_mm指向了mm_struct結構。這個結構是整個進程虛存空間的數據抽象。它管理這進程中的所有vm_area_struct抽象,換句話mm_struct是一個更高層的數據結構。定義例如如下:
[include/linux/mm.h : 203]

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 };

204: mmap做爲進程的所虛存區域構成的鏈表的鏈表頭。
205: mmap_avl指向進程所有虛存區域構成的AVL樹的根節點。


206: mmap_cache指向近期一次使用的虛存空間。因爲程序具備局部性。常常連續訪問同一片區域的空間。因此這裏記錄了近期一次使用的虛存區域。是一種緩存機制。
207: pgd指向了進程的頁文件夾
208~212:
儘管每個進程僅僅能擁有一個mm_struct,但是一個mm_struct卻能爲多個進程所用。最顯著的樣例就是父進程使用vfork建立子進程。這樣就會涉及到資源進程和引用計數的問題,當中mm_users、mm_count、map_count做爲引用計數,而mmap_sem、page_table_lock則用來保證訪問的相互排斥。


216~219:用於說明整個進程地址空間中各個段的起始和結束地址,比方代碼段。數據段,棧段。環境變量段等等。(注,這裏的段是指鏡像段,不能與段頁管理中的段相混淆)

3.3 進程地址空間和進程虛存區域的關係

每個進程都擁有一份mm_struct,它是整個3G進程空間的數據抽象,而因爲進程每每不會整個3G空間都使用,而是使用不一樣的離散虛存區域。並且每個離散區域的使用都不同,比方說棧所在的區域和代碼段所在的區域的使用就不同,從而致使各個區域的屬性、需要的管理操做都不一致,因此就將各個虛存區域提煉出來單獨管理,而後就再將所有虛存區域組成一顆AVL樹交由mm_struct統一管理。這中設計思想屬於提煉差別,或者說提煉子類的思想。

相關文章
相關標籤/搜索