Linux虛擬內存組織結構淺析(二)

Linux虛擬內存組織結構()<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

在前一篇文章中我們介紹了Linux虛擬內存在邏輯上的組織結構,現在就讓我們從源代碼入手,從程序級仔細看看各個數據結構體的內部組成如何,源代碼來自於最新的kernel2.6.26.5,分析過程中主要參考了《Understanding the linux virtual memory》這本書,有興趣的朋友可以去閱讀一下。

 

一、節點的數據表示

在內核中,節點由結構體pg_data_t來表示,它是由結構體pglist_data通過typedef來定義的,位於文件<linux/mmzone.h>,其內容如下:

typedef struct pglist_data {

   struct zone node_zones[MAX_NR_ZONES];

   struct zonelist node_zonelists[MAX_ZONELISTS];

   int nr_zones;

#ifdef CONFIG_FLAT_NODE_MEM_MAP

   struct page *node_mem_map;

#endif

   struct bootmem_data *bdata;

#ifdef CONFIG_MEMORY_HOTPLUG

   spinlock_t node_size_lock;

#endif

   unsigned long node_start_pfn;

   unsigned long node_present_pages; /* total number of physical pages */

   unsigned long node_spanned_pages; /* total size of physical page

                        range, including holes */

   int node_id;

   wait_queue_head_t kswapd_wait;

   struct task_struct *kswapd;

   int kswapd_max_order;

} pg_data_t;

 

各字段含義如下:

node_zones:包含本節點內區的描述結構體,一般而言MAX_NR_ZONES爲3。

node_zonelists:這個數組指明瞭在分配內存時區的選擇順序。

nr_zones:指明本節點內區的個數,這一般都是1到3中的一個數字。並不是所有的節點都有3個區,比如有些節點就沒有ZONE_DMA區。

node_mem_map:指向全局內存描述數組中的某一項,該元素描述了本節點的第一頁物理內存。

bdata:指向bootmem_data結構體,該結構體用於系統啓動時的內存分配器分配、管理該節點的內存。該內存分配器在系統啓動完畢後不再使用。

node_size_lock:該自旋鎖用於保護node_start_pfn、node_present_pages以及node_spanned_pages,當你要取得這些變量的值時,必須首先獲取這個自旋鎖。當你調用pfn_valid()前也應當獲得這個自旋鎖。並且注意必須在zone->lock和zone->size_seqlock前獲取這個自旋鎖。

node_start_pfn:這個是該節點內第一頁物理內存在全局數組mem_map中的序號。

node_present_pages:當前該節點內可獲得的物理頁總數。

node_spanned_pages:節點內可訪問的所有物理頁數,包含可能存在的空洞頁。

node_id:當前節點的編號,從0開始。

kswapd_wait:kswapd線程的等待隊列。kswapd是一個內核線程,當系統中內存頁較少時它負責回收物理內存頁,這個線程常常處於睡眠狀態。

kswapd:指向kswapd線程的進程描述符。

kswapd_max_order:kswapd線程要釋放內存塊大小取對數後的值。

 

二、區的數據表示

每個區由結構體struct zone來描述,其中包含了頁使用統計、空閒頁數、瑣等信息,這個結構體在<linux/mmzone.h>中定義:

struct zone {

   unsigned long       pages_min, pages_low, pages_high;

   unsigned long       lowmem_reserve[MAX_NR_ZONES];

#ifdef CONFIG_NUMA

   int node;

   unsigned long       min_unmapped_pages;

   unsigned long       min_slab_pages;

   struct per_cpu_pageset  *pageset[NR_CPUS];

#else

   struct per_cpu_pageset  pageset[NR_CPUS];

#endif

   spinlock_t      lock;

#ifdef CONFIG_MEMORY_HOTPLUG

   seqlock_t       span_seqlock;

#endif

   struct free_area    free_area[MAX_ORDER];

 

#ifndef CONFIG_SPARSEMEM

   unsigned long       *pageblock_flags;

#endif /* CONFIG_SPARSEMEM */

 

   spinlock_t      lru_lock;  

   struct list_head    active_list;

   struct list_head    inactive_list;

   unsigned long       nr_scan_active;

   unsigned long       nr_scan_inactive;

   unsigned long       pages_scanned;     /* since last reclaim */

   unsigned long       flags;

   atomic_long_t       vm_stat[NR_VM_ZONE_STAT_ITEMS];

   int prev_priority;

   wait_queue_head_t   * wait_table;

   unsigned long       wait_table_hash_nr_entries;

   unsigned long       wait_table_bits;

   struct pglist_data  *zone_pgdat;

   unsigned long       zone_start_pfn;

   unsigned long       spanned_pages; 

   unsigned long       present_pages; 

   const char      *name;

} ____cacheline_internodealigned_in_smp;

 

各字段含義如下:

pages_min:管理區中保留的頁數,當區中空閒的頁框數達到這個值時kswapd線程會被喚醒並以同步的方式回收區中的頁框。

pages_low:回收頁框的下限,當區中空閒的頁框數達到這個值時,夥伴分配器會喚起kswapd線程開始回收頁框。

pages_high:回收頁框的上限,當kswapd線程被喚醒回收頁框時,它會一直回收頁框直到區中空閒的頁框數達到這個值爲止。

lowmem_reserve:指明在靠近內存低端的區內需要保留的頁框數,這個主要是爲了防止這樣一種情況:當內存低端的頁框全部被分配後,如果這時候有新的頁分配請求就可能會產生OOM,即使此刻在高端內存我們還有大量空閒的頁框。

node:指明這個區所屬的節點編號。

pageset:用於實現每cpu高速緩存的數組,每個cpu在該數組內佔有一項。由於內核經常請求和釋放單個頁框,因此每cpu高速緩存包含一些預先分配的頁框,它們用於滿足本地cpu發出的單一內存請求。

lock保護該描述符的自旋鎖。

span_seqlock:保護zone_start_pfn、spanned_pages和present_pages的順序鎖,之所以使用順序鎖來保護這3個變量是因爲我們經常需要在沒有獲得zone->lock的情況下讀取它們的值,同時我們很少更改這些值。

free_area:標識出區中空閒頁框快,這個結構是夥伴分配器的主要數據結構,夥伴分配器就是根據該結構體來對區中內存進行分配、回收等操作的。

pageblock_flags:一個內存塊的標誌,該內存塊大小爲pageblock_nr_pages。

lru_lock:活動及非活動頁鏈表使用的自旋鎖,主要在頁框回收時使用。

active_list:區的活動頁鏈表。

inactive_list:區的非活動頁鏈表。

nr_scan_active:回收頁框要掃描的活動頁數目。

nr_scan_inactive:回收頁框要掃描的非活動頁數目。

pages_scanned:頁框回收時使用的計數器。

flags:區的標誌,目前有三個標誌可以設置:

ZONE_ALL_UNRECLAIMABLE——區中所有頁框被鎖定不能回收

ZONE_RECLAIM_LOCKED——不能併發回收區中頁框

ZONE_OOM_LOCKED——區處於OOM的區鏈表中

vm_stat:包含區的統計信息。

prev_priority:區掃描優先級,主要用於頁框回收過程中。

wait_table:進程等待隊列的hash表,表中的進程在等待區中的某一頁內存。

wait_table_hash_nr_entries:等待隊列hash表數組的大小。

wait_table_bits:等待隊列hash表大小,1<<wait_table_bits。

zone_pgdat:指向區所屬節點的指針。

zone_start_pfn:區中第一個頁框在mem_map數組中的編號。

spanned_pages:區中的頁框數,包含空洞。

present_pages:區中包含的頁框數,不包含空洞。

name:指向區的名稱,這個域很少使用。

 

三、頁的數據表示

這個結構體是描述物理內存的最小單位,它描述了每個物理頁框的屬性,定義於文件<linux/mm_types.h>:

struct page {

   unsigned long flags;       

   atomic_t _count;   

   union {

       atomic_t _mapcount;

       struct {       

           u16 inuse;

           u16 objects;

       };

   };

   union {

       struct {

       unsigned long private;     

       struct address_space *mapping; 

       };

#if NR_CPUS >= CONFIG_SPLIT_PTLOCK_CPUS

       spinlock_t ptl;

#endif

       struct kmem_cache *slab;

       struct page *first_page;

   };

   union {

       pgoff_t index;      /* Our offset within mapping. */

       void *freelist;     /* SLUB: freelist req. slab lock */

   };

   struct list_head lru;      

 

#if defined(WANT_PAGE_VIRTUAL)

   void *virtual;         

#endif

#ifdef CONFIG_CGROUP_MEM_RES_CTLR

   unsigned long page_cgroup;

#endif

};

各字段含義如下:

flags:一組原子型標誌,有些標誌可以異步更新,這個域功能繁多,有興趣的可以進一步看看源代碼。

_count:頁框的引用計數器。如果該字段爲-1,則相應頁框空閒,並可被分配給任一進程或內核本身;如果該字段大於或等於0,則說明該頁框被分配給了一個或多個進程,或用於存放一些內核數據結構。

_mapcount:本頁框對應的頁表項數目,本頁框若未被映射則爲-1。

inuse、objects:這2個標誌在SLUB分配器中使用。

private:可用於正在使用該頁的內核成分使用,如果頁是空閒的則該字段由夥伴系統使用。

mapping:當頁被插入頁高速緩存中時使用。

slab:指向slab的指針。

first_page:

index、freelist:作爲不同的含義被幾種內核成分使用。

lru:將頁鏈接到最近最少使用雙向鏈表中。

virtual:指向本頁框的內核虛擬地址。

page_cgroup:這個域只有在內存資源控制器起作用的情況下使用,用於統計處於內存資源控制器下的頁框。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章