天天看點

linux核心源碼解析–page資料結構

作者:核心中文社群

幾個問題:

1.當開啟了MMU之後,CPU通路記憶體的最小機關是多少呢?

page

2.linux怎樣描述這個頁呢?

3.linux核心裡,怎麼了解和使用這個頁?

linux核心用stuct page來描述一個實體頁面:

1.簡化版的page結構體

/*
 * page描述一個實體頁面
 */
struct page {
    unsigned long flags;
    atomic_t      _refcount;
    atomic_t      _mapcount;
    unsigned long private;
    struct address_space *mapping; 
    pgoff_t       index;
    struct list_head lru;
    void *        *virtual;
} _struct_page_alignment;           

flags字段:

enum pageflags {
    PG_locked,      /* Page is locked. Don't touch. */ ///表示頁面已經上鎖;如果該比特位置位,說明已經被鎖,記憶體管理其他子產品不能通路這個頁面,防止競争
    PG_referenced, ///同PG_active一起,用于控制頁面的活躍程度,在kswapd頁面回收中使用;
    PG_uptodate,   ///表示頁面的資料已經從塊裝置成功讀取到記憶體頁面;
    PG_dirty,      ///表示頁面内容發生改變,這個頁面為髒的,即頁面内容被改寫,還沒同步到外部存儲器
    PG_lru,        ///表示頁面加入了LRU連結清單中,核心使用LRU連結清單來管理活躍和不活躍頁面;
    PG_active,
    PG_workingset,
    PG_waiters,     /* Page has waiters, check its waitqueue. Must be bit #7 and in the same byte as "PG_locked" */
    PG_error,       /////表示頁面操作過程中發生錯誤時會設定該位;
    PG_slab,        ///頁面用于slab配置設定器
    PG_owner_priv_1,    /* Owner use. If pagecache, fs may use*/
    PG_arch_1,
    PG_reserved,
    PG_private,     /* If pagecache, has fs-private data */
    PG_private_2,       /* If pagecache, has fs aux data */
    PG_writeback,       /* Page is under writeback */  ///表示頁面的内容正在向塊裝置進行會寫
    PG_head,        /* A head page */
    PG_mappedtodisk,    /* Has blocks allocated on-disk */
    PG_reclaim,     /* To be reclaimed asap */           ///表示這個頁面馬上要被回收
    PG_swapbacked,      /* Page is backed by RAM/swap */ ///表示頁面具有swap緩存功能,通過匿名頁面才可以寫回swap分區
    PG_unevictable,     /* Page is "unevictable"  */     ///表示這個頁面不能回收
#ifdef CONFIG_MMU
    PG_mlocked,     /* Page is vma mlocked */   ///表示頁面對應的vma處于mlocked狀态;
#endif
#ifdef CONFIG_ARCH_USES_PG_UNCACHED
    PG_uncached,        /* Page has been mapped as uncached */
#endif
#ifdef CONFIG_MEMORY_FAILURE
    PG_hwpoison,        /* hardware poisoned page. Don't touch */
#endif
#if defined(CONFIG_IDLE_PAGE_TRACKING) && defined(CONFIG_64BIT)
    PG_young,
    PG_idle,
#endif
#ifdef CONFIG_64BIT
    PG_arch_2,
#endif
    __NR_PAGEFLAGS,
 
    /* Filesystems */
    PG_checked = PG_owner_priv_1,
 
    /* SwapBacked */
    ///表示頁面處于交換緩存
    PG_swapcache = PG_owner_priv_1, /* Swap page: swp_entry_t in private */
 
    /* Two page bits are conscripted by FS-Cache to maintain local caching
     * state.  These bits are set on pages belonging to the netfs's inodes
     * when those inodes are being locally cached.
     */
    PG_fscache = PG_private_2,  /* page backed by cache */
 
    /* XEN */
    /* Pinned in Xen as a read-only pagetable page. */
    PG_pinned = PG_owner_priv_1,
    /* Pinned as part of domain save (see xen_mm_pin_all()). */
    PG_savepinned = PG_dirty,
    /* Has a grant mapping of another (foreign) domain's page. */
    PG_foreign = PG_owner_priv_1,
    /* Remapped by swiotlb-xen. */
    PG_xen_remapped = PG_owner_priv_1,
 
    /* SLOB */
    PG_slob_free = PG_private,
 
    /* Compound pages. Stored in first tail page's flags */
    PG_double_map = PG_workingset,
 
    /* non-lru isolated movable page */
    PG_isolated = PG_reclaim,
 
    /* Only valid for buddy pages. Used to track pages that are reported */
    PG_reported = PG_uptodate,
};           
linux核心源碼解析–page資料結構

需要的小夥伴私信回複核心免費領取

flags布局

flags成員除了上述重要标志位之外,還有個重要作用,就是存放SECTION編号,node節點編号,zone編号等;

比如在ARM Vexpress平台中page->flags布局圖,bit[0:43]用來存放頁面标志位,bit[44:59]用于NUMA平很算法中的LAST_CPUPID,bit[60:61]用于存放zone編号bit[62:63]存放node編号;

|63__node__62|61__zone__60|59__LAST_CPUPID__44|43__flags___0|

linux核心裡一般不直接操作變量,linux提供了一些列的接口函數,盡量使用這些封裝好的接口;

static inline struct zone *page_zone(const struct page *page)
    static inline void set_page_zone(struct page *page, enum zone_type zone)           

_refcount字段:

當_refcount等于0時,表示該page頁面為空閑或即将要被釋放的頁面;

當_refcount大于0時,表示該page頁面已經被配置設定且核心正在使用,暫時不會被釋放

static inline void get_page(struct page *page)  ///_refcount加一
static inline void put_page(struct page *page) ///_refcount減一,若_refcount==0,會釋放該頁面
static inline int page_count(struct page *page)     ///統計_count個數           

_refcount使用場景

(1)初始狀态,空閑頁面,_refcount=0;

(2)配置設定頁面時,_refcount會變成1;

(3)加入LRU連結清單時,頁面會被kswapd核心線程使用,_refcount會加1;

添加到LRU連結清單後,_refcount減1,防止頁面在添加到LRU過程中被釋放;

(4)被映射到其他使用者程序的PTE時,_refcount會加1.

(5)頁面的private成員指向私有資料;

對于PG_swapable的頁面,__add_to_swap_cache()增加_refcount;

對于PG_private頁面,主要在塊裝置的buffer_head中使用,如buffer_migrate_page()會增加_refcount;

(6)核心對頁表進行操作等關鍵路徑上也會使_refcount加1.

_mapcount字段:

_mapcount引用計數,表示這個頁面被程序映射的個數,即已經映射了多少個使用者pte頁表。

在ARM64的Linux核心中,每個使用者程序都擁有一個獨立的虛拟位址空間和獨立的頁表,是以有可能出現多個使用者程序虛拟位址映射到一個實體頁面,RMAP反向映射系統就是利用這個特性來實作的。

_mapcount引用計數,主要用于RMAP反向映射系統中;

_mapcount**-1,表示沒有pte映射到頁面中;**

_mapcount0,表示隻有父程序映射了頁面,匿名頁面剛配置設定時,_mapcount引用計數初始化為0;

通路:

page_dup_rmap();///增加_refcount
static inline int page_mapcount(struct page *page); ///統計_mapcount個數           

mmaping字段:

對于匿名頁面,mapping指向VMA的anon_vma結構;

對于交換高速緩存頁面,mapping指向交換分區的swapper_spaces;

對于檔案映射頁面,mapping指向該檔案所屬的address_space結構,它包含檔案所屬的媒體相關資訊,如inode節點,節點對應操作方法等;

address_space為8位元組對齊,低兩位用于其它用途:

bit[0]:判斷該頁面是否為匿名頁面;

bit[1]:判斷該頁面是否為非LRU頁面;

bit[01]0b11,表示這是一個KSM頁面;

static __always_inline int PageAnon(struct page *page)        ///判斷是否為匿名頁面
static __always_inline int __PageMovable(struct page *page)   ///判斷是否為非LRU頁面
static __always_inline int PageKsm(struct page *page)         ///判斷是否為KSM頁面
 
void *page_rmapping(struct page *page)                        ///傳回mapping成員,清除低2位;
struct address_space *page_mapping(struct page *page)      ///傳回mapping成員指向的位址空間
bool page_mapped(struct page *page)                         ///判斷該頁面是否映射到使用者PTE           

lru字段:

用在頁面回收的LRU連結清單算法中,LRU連結清單算法定義了多個連結清單;

用來把一個slab添加到slab滿連結清單、slab空閑連結清單或slab部分連結清單中;

virtual字段:

一個指向頁所對應的虛拟位址的指針;

頁面鎖PG_Locked:

struct page資料結構成員flags定義了一個标志PG_locked;核心通常用PG_locked來設定一個頁面鎖;

static inline void lock_page(struct page *page)  ///用于申請頁面鎖,如果頁面鎖被其他程序占用,那麼會睡眠等待;
 
static inline int trylock_page(struct page *page)   ///如果傳回false表示擷取鎖失敗,傳回true表示擷取鎖成功;不會睡眠           

2.struct page 的意義;

核心使用struct page來描述一個實體頁面,我們看到了管理這些頁面的資訊,比如:

(1)核心知道目前這個頁面的狀态(通過flags字段);

(2)核心需要知道一個頁面是否空閑,即有沒有配置設定出去,有多少個程序(_count)或記憶體路徑通路了這個頁面(_mapcount);

(3)核心知道誰在使用這個頁面,使用者是使用者空間程序的匿名頁,還是page cache(mapping);

(4)核心知道這個頁面是否被slab即使使用(lru, s_mem等字段);

(5)核心知道這個頁面是否線性映射(virtual);

3.struct page資料結構存放在哪裡?

page存放在一個全局數組mem_map[]中;

linux核心源碼解析–page資料結構

注意:存放的是struct page結構體,不是指針;

4.總結

Linux核心的記憶體管理以page頁面為核心,struct page資料結構提供了很多字段,其中_refcount和_mapcount是兩個非常重要的引用計數,正确了解它們是了解Linux核心記憶體管理的基石。

_refcount是page頁面的“命根子”;

_mapcount是page頁面的“幸福指數”

struct page是Linux核心最重要的資料結構之一,想深入研究Linux記憶體管理,有必要慢慢研究struct page中重要成員的含義和用法。

繼續閱讀