幾個問題:
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,
};
需要的小夥伴私信回複核心免費領取
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[]中;
注意:存放的是struct page結構體,不是指針;
4.總結
Linux核心的記憶體管理以page頁面為核心,struct page資料結構提供了很多字段,其中_refcount和_mapcount是兩個非常重要的引用計數,正确了解它們是了解Linux核心記憶體管理的基石。
_refcount是page頁面的“命根子”;
_mapcount是page頁面的“幸福指數”
struct page是Linux核心最重要的資料結構之一,想深入研究Linux記憶體管理,有必要慢慢研究struct page中重要成員的含義和用法。