Linux內存描述以內存頁面page--Linux內存管理(四)

1 Linux如何描述物理內存

Linux把物理內存劃分爲三個層次來管理node

層次 描述
存儲節點(Node) CPU被劃分爲多個節點(node), 內存則被分簇, 每一個CPU對應一個本地物理內存, 即一個CPU-node對應一個內存簇bank,即每一個內存簇被認爲是一個節點
管理區(Zone) 每一個物理內存節點node被劃分爲多個內存管理區域, 用於表示不一樣範圍的內存, 內核可使用不一樣的映射方式映射物理內存
頁面(Page) 內存被細分爲多個頁面幀, 頁面是最基本的頁面分配的單位 
  • 首先內存被劃分爲結點. 內存中的每一個節點都是由pg_data_t描述,而pg_data_tstruct pglist_data定義而來, 該數據結構定義在include/linux/mmzone.h, line 615, 每一個結點關聯到系統中的一個處理器, 內核中表示爲pg_data_t的實例. 系統中每一個節點被連接到一個以NULL結尾的pgdat_list鏈表中<而其中的每一個節點利用pg_data_tnode_next字段連接到下一節.而對於PC這種UMA結構的機器來講, 只使用了一個成爲contig_page_data的靜態pg_data_t結構.
  • 接着各個節點又被劃分爲內存管理區域, 一個管理區域經過struct zone_struct描述, 其被定義爲zone_t, 用以表示內存的某個範圍, 低端範圍的16MB被描述爲ZONE_DMA, 某些工業標準體系結構中的(ISA)設備須要用到它, 而後是可直接映射到內核的普通內存域ZONE_NORMAL,最後是超出了內核段的物理地址域ZONE_HIGHMEM, 被稱爲高端內存. 是系統中預留的可用內存空間, 不能被內核直接映射.
  • 最後頁幀(page frame)表明了系統內存的最小單位, 堆內存中的每一個頁都會建立一個struct page的一個實例. 傳統上,把內存視爲連續的字節,即內存爲字節數組,內存單元的編號(地址)可做爲字節數組的索引. 分頁管理時,將若干字節視爲一頁,好比4K byte. 此時,內存變成了連續的頁,即內存爲頁數組,每一頁物理內存叫頁幀,以頁爲單位對內存進行編號,該編號可做爲頁數組的索引,又稱爲頁幀號.

2 頁幀struct page

分頁單元能夠實現把線性地址轉換爲物理地址, 爲了效率起見, 線性地址被分爲固定長度爲單位的組, 稱爲」頁」, 頁內部的線性地址被映射到連續的物理地址. 這樣內核能夠指定一個頁的物理地址和其存儲權限, 而不用指定頁所包含的所有線性地址的存儲權限.linux

分頁單元把全部RAM分爲固定長度的頁幀(也叫頁框, 物理頁, 英文page frame). 每個頁幀包含一個頁(page). 也就是說一個頁幀的長度與一個頁的長度一致. 頁框是主存的一部分, 所以也是一個存儲區域. 簡單來講, 頁是一個數據塊, 能夠存放在任何頁框(內存中)或者磁盤(被交換至交換分區)中算法

咱們今天就來詳細講解一下linux下物理頁幀的描述c#

2 頁幀

內核把物理頁做爲內存管理的基本單位. 儘管處理器的最小可尋址單位一般是字, 可是, 內存管理單元MMU一般以頁爲單位進行處理. 所以,從虛擬內存的上來看,頁就是最小單位.數組

頁幀表明了系統內存的最小單位, 對內存中的每一個頁都會建立struct page的一個實例. 內核必需要保證page結構體足夠的小,不然僅struct page就要佔用大量的內存.緩存

由於即便在中等程序的內存配置下, 系統的內存一樣會分解爲大量的頁. 例如, IA-32系統中標準頁長度爲4KB, 在內存大小爲384MB時, 大約有100000頁. 就當今的標準而言, 這個容量算不上很大, 但頁的數目已經很是可觀了數據結構

於是出於節省內存的考慮,內核要盡力保持struct page儘量的小. 在典型的系統中, 因爲頁的數目巨大, 所以對page結構的小改動, 也可能致使保存全部page實例所需的物理內存暴漲.app

頁的普遍使用, 增長了保持結構長度的難度 : 內存管理的許多部分都使用頁, 用於各類不一樣的用途. 內核的一部分可能徹底依賴於struct page提供的特定信息, 而這部分信息堆內核的其餘部分頁多是徹底無用的. 等等.dom

2.1 struct page結構

內核用struct page(include/linux/mm_types.h?v=4.7, line 45)結構表示系統中的每一個物理頁.electron

出於節省內存的考慮,struct page中使用了大量的聯合體union.

/*
 * Each physical page in the system has a struct page associated with
 * it to keep track of whatever it is we are using the page for at the
 * moment. Note that we have no way to track which tasks are using
 * a page, though if it is a pagecache page, rmap structures can tell us
 * who is mapping it.
 *
 * The objects in struct page are organized in double word blocks in
 * order to allows us to use atomic double word operations on portions
 * of struct page. That is currently only used by slub but the arrangement
 * allows the use of atomic double word operations on the flags/mapping
 * and lru list pointers also.
 */
struct page {
    /* First double word block */
    unsigned long flags;        /* Atomic flags, some possibly updated asynchronously
                                              描述page的狀態和其餘信息  */
    union
    {
        struct address_space *mapping;  /* If low bit clear, points to
                         * inode address_space, or NULL.
                         * If page mapped as anonymous
                         * memory, low bit is set, and
                         * it points to anon_vma object:
                         * see PAGE_MAPPING_ANON below.
                         */
        void *s_mem;            /* slab first object */
        atomic_t compound_mapcount;     /* first tail page */
        /* page_deferred_list().next     -- second tail page */
    };

    /* Second double word */
    struct {
        union {
            pgoff_t index;      /* Our offset within mapping.
            在映射的虛擬空間(vma_area)內的偏移;
            一個文件可能只映射一部分,假設映射了1M的空間,
            index指的是在1M空間內的偏移,而不是在整個文件內的偏移。 */
            void *freelist;     /* sl[aou]b first free object */
            /* page_deferred_list().prev    -- second tail page */
        };

        union {
#if defined(CONFIG_HAVE_CMPXCHG_DOUBLE) && \
    defined(CONFIG_HAVE_ALIGNED_STRUCT_PAGE)
            /* Used for cmpxchg_double in slub */
            unsigned long counters;
#else
            /*
             * Keep _refcount separate from slub cmpxchg_double
             * data.  As the rest of the double word is protected by
             * slab_lock but _refcount is not.
             */
            unsigned counters;
#endif

            struct {

                union {
                    /*
                     * Count of ptes mapped in mms, to show
                     * when page is mapped & limit reverse
                     * map searches.
                     * 頁映射計數器
                     */
                    atomic_t _mapcount;

                    struct { /* SLUB */
                        unsigned inuse:16;
                        unsigned objects:15;
                        unsigned frozen:1;
                    };
                    int units;      /* SLOB */
                };
                /*
                 * Usage count, *USE WRAPPER FUNCTION*
                 * when manual accounting. See page_ref.h
                 * 頁引用計數器
                 */
                atomic_t _refcount;
            };
            unsigned int active;    /* SLAB */
        };
    };

    /*
     * Third double word block
     *
     * WARNING: bit 0 of the first word encode PageTail(). That means
     * the rest users of the storage space MUST NOT use the bit to
     * avoid collision and false-positive PageTail().
     */
    union {
        struct list_head lru;   /* Pageout list, eg. active_list
                     * protected by zone->lru_lock !
                     * Can be used as a generic list
                     * by the page owner.
                     */
        struct dev_pagemap *pgmap; /* ZONE_DEVICE pages are never on an
                        * lru or handled by a slab
                        * allocator, this points to the
                        * hosting device page map.
                        */
        struct {        /* slub per cpu partial pages */
            struct page *next;      /* Next partial slab */
#ifdef CONFIG_64BIT
            int pages;      /* Nr of partial slabs left */
            int pobjects;   /* Approximate # of objects */
#else
            short int pages;
            short int pobjects;
#endif
        };

        struct rcu_head rcu_head;       /* Used by SLAB
                         * when destroying via RCU
                         */
        /* Tail pages of compound page */
        struct {
            unsigned long compound_head; /* If bit zero is set */

            /* First tail page only */
#ifdef CONFIG_64BIT
            /*
             * On 64 bit system we have enough space in struct page
             * to encode compound_dtor and compound_order with
             * unsigned int. It can help compiler generate better or
             * smaller code on some archtectures.
             */
            unsigned int compound_dtor;
            unsigned int compound_order;
#else
            unsigned short int compound_dtor;
            unsigned short int compound_order;
#endif
        };

#if defined(CONFIG_TRANSPARENT_HUGEPAGE) && USE_SPLIT_PMD_PTLOCKS
        struct {
            unsigned long __pad;    /* do not overlay pmd_huge_pte
                         * with compound_head to avoid
                         * possible bit 0 collision.
                         */
            pgtable_t pmd_huge_pte; /* protected by page->ptl */
        };
#endif
    };

    /* Remainder is not double word aligned */
    union {
        unsigned long private;      /* Mapping-private opaque data:
                         * usually used for buffer_heads
                         * if PagePrivate set; used for
                         * swp_entry_t if PageSwapCache;
                         * indicates order in the buddy
                         * system if PG_buddy is set.
                         * 私有數據指針,由應用場景肯定其具體的含義
                         */
#if USE_SPLIT_PTE_PTLOCKS
#if ALLOC_SPLIT_PTLOCKS
        spinlock_t *ptl;
#else
        spinlock_t ptl;
#endif
#endif
        struct kmem_cache *slab_cache;  /* SL[AU]B: Pointer to slab */
    };

#ifdef CONFIG_MEMCG
    struct mem_cgroup *mem_cgroup;
#endif

    /*
     * On machines where all RAM is mapped into kernel address space,
     * we can simply calculate the virtual address. On machines with
     * highmem some memory is mapped into kernel virtual memory
     * dynamically, so we need a place to store that address.
     * Note that this field could be 16 bits on x86 ... ;)
     *
     * Architectures with slow multiplication can define
     * WANT_PAGE_VIRTUAL in asm/page.h
     */
#if defined(WANT_PAGE_VIRTUAL)
    void *virtual;          /* Kernel virtual address (NULL if
                       not kmapped, ie. highmem) */
#endif /* WANT_PAGE_VIRTUAL */

#ifdef CONFIG_KMEMCHECK
    /*
     * kmemcheck wants to track the status of each byte in a page; this
     * is a pointer to such a status block. NULL if not tracked.
     */
    void *shadow;
#endif

#ifdef LAST_CPUPID_NOT_IN_PAGE_FLAGS
    int _last_cpupid;
#endif
}
/*
 * The struct page can be forced to be double word aligned so that atomic ops
 * on double words work. The SLUB allocator can make use of such a feature.
 */
#ifdef CONFIG_HAVE_ALIGNED_STRUCT_PAGE
    __aligned(2 * sizeof(unsigned long))
#endif
;
字段 描述
flag 用來存放頁的狀態,每一位表明一種狀態,因此至少能夠同時表示出32中不一樣的狀態,這些狀態定義在linux/page-flags.h中
virtual 對於若是物理內存能夠直接映射內核的系統, 咱們能夠之間映射出虛擬地址與物理地址的管理, 可是對於須要使用高端內存區域的頁, 即沒法直接映射到內核的虛擬地址空間, 所以須要用virtual保存該頁的虛擬地址
_refcount 引用計數,表示內核中引用該page的次數, 若是要操做該page, 引用計數會+1, 操做完成-1. 當該值爲0時, 表示沒有引用該page的位置,因此該page能夠被解除映射,這每每在內存回收時是有用的
_mapcount 被頁表映射的次數,也就是說該page同時被多少個進程共享。初始值爲-1,若是隻被一個進程的頁表映射了,該值爲0. 若是該page處於夥伴系統中,該值爲PAGE_BUDDY_MAPCOUNT_VALUE(-128),內核經過判斷該值是否爲PAGE_BUDDY_MAPCOUNT_VALUE來肯定該page是否屬於夥伴系統
index 在映射的虛擬空間(vma_area)內的偏移;一個文件可能只映射一部分,假設映射了1M的空間,index指的是在1M空間內的偏移,而不是在整個文件內的偏移
private 私有數據指針,由應用場景肯定其具體的含義
lru 鏈表頭,用於在各類鏈表上維護該頁, 以便於按頁將不一樣類別分組, 主要有3個用途: 夥伴算法, slab分配器, 被用戶態使用或被當作頁緩存使用
mapping 指向與該頁相關的address_space對象
index 頁幀在映射內部的偏移量

注意區分_count和_mapcount,_mapcount表示的是映射次數,而_count表示的是使用次數;被映射了不必定在使用,但要使用必須先映射。

2.2 mapping & index

mapping指定了頁幀所在的地址空間, index是頁幀在映射內部的偏移量. 地址空間是一個很是通常的概念. 例如, 能夠用在向內存讀取文件時. 地址空間用於將文件的內容與裝載數據的內存區關聯起來. mapping不只可以保存一個指針, 並且還能包含一些額外的信息, 用於判斷頁是否屬於未關聯到地址空間的某個匿名內存區.

  1. 若是mapping = 0,說明該page屬於交換高速緩存頁(swap cache);當須要使用地址空間時會指定交換分區的地址空間swapper_space。
  2. 若是mapping != 0,第0位bit[0] = 0,說明該page屬於頁緩存或文件映射,mapping指向文件的地址空間address_space。
  3. 若是mapping != 0,第0位bit[0] != 0,說明該page爲匿名映射,mapping指向struct anon_vma對象。

經過mapping恢復anon_vma的方法:anon_vma = (struct anon_vma *)(mapping - PAGE_MAPPING_ANON)。

pgoff_t index是該頁描述結構在地址空間radix樹page_tree中的對象索引號即頁號, 表示該頁在vm_file中的偏移頁數, 其類型pgoff_t被定義爲unsigned long即一個機器字長.

/*
 * The type of an index into the pagecache.
 */
#define pgoff_t unsigned long

2.3 private私有數據指針

private私有數據指針, 由應用場景肯定其具體的含義:

  1. 若是設置了PG_private標誌,則private字段指向struct buffer_head
  2. 若是設置了PG_compound,則指向struct page
  3. 若是設置了PG_swapcache標誌,private存儲了該page在交換分區中對應的位置信息swp_entry_t。
  4. 若是_mapcount = PAGE_BUDDY_MAPCOUNT_VALUE,說明該page位於夥伴系統,private存儲該夥伴的階

2.4 lru鏈表頭

最近、最久未使用struct slab結構指針變量

lru:鏈表頭,主要有3個用途:

  1. 則page處於夥伴系統中時,用於連接相同階的夥伴(只使用夥伴中的第一個page的lru便可達到目的)。
  2. 設置PG_slab, 則page屬於slab,page->lru.next指向page駐留的的緩存的管理結構,page->lru.prec指向保存該page的slab的管理結構。
  3. page被用戶態使用或被當作頁緩存使用時,用於將該page連入zone中相應的lru鏈表,供內存回收時使用。

3 體系結構無關的頁面的狀態flags

頁的不一樣屬性經過一系列頁標誌描述, 存儲在struct page的flag成員中的各個比特位.

struct page {
    /* First double word block */
    unsigned long flags;        /* Atomic flags,
    some possibly updated asynchronously, 描述page的狀態和其餘信息  */

這些標識是獨立於體系結構的, 於是沒法經過特定於CPU或計算機的信息(該信息保存在頁表中)

3.1 頁面到管理區和節點的映射

在早期的linux-2.4.18的內核中, struct page存儲有一個指向對應管理區的指針page->zone, 可是該這hi真在吼吼被認爲是一種浪費, 由於若是有成千上萬的這樣的struct page存在, 那麼即便是很小的指針也會消耗大量的內存空間.

所以在後來linux-2.4.x的更新中, 刪除了這個字段, 取而代之的是page->flags的最高ZONE_SHIFT位和NODE_SHIFT位, 存儲了其所在zone和node在內存區域表zone_table的編號索引.

那麼內核在初始化內存管理區時, 首先創建管理區表zone_table. 參見mm/page_alloc.c?v=2.4.37, line 38

/*
 *
 * The zone_table array is used to look up the address of the
 * struct zone corresponding to a given zone number (ZONE_DMA,
 * ZONE_NORMAL, or ZONE_HIGHMEM).
 */
zone_t *zone_table[MAX_NR_ZONES*MAX_NR_NODES];
EXPORT_SYMBOL(zone_table);

MAX_NR_ZONES是一個節點中所能包容納的管理區的最大數, 如3個, 定義在include/linux/mmzone.h?v=2.4.37, line 25, 與zone區域的類型(ZONE_DMA, ZONE_NORMAL, ZONE_HIGHMEM)定義在一塊兒. 固然這時候咱們這些標識都是經過宏的方式來實現的, 而不是現在的枚舉類型

MAX_NR_NODES是能夠存在的節點的最大數.

函數EXPORT_SYMBOL使得內核的變量或者函數能夠被載入的模塊(好比咱們的驅動模塊)所訪問.

該表處理起來就像一個多維數組, 在函數free_area_init_core中, 一個節點的全部頁面都會被初始化.

內核提供了page_zone經過頁面查找其對應的內存區域zone_t, 頁提供了set_page_zone接口, 而查找到了zone後, 能夠經過 其struct pglist_data *zone_pgdat直接獲取其所在node信息

/*
 * The zone field is never updated after free_area_init_core()
 * sets it, so none of the operations on it need to be atomic.
 */
#define NODE_SHIFT 4
#define ZONE_SHIFT (BITS_PER_LONG - 8)

struct zone_struct;
extern struct zone_struct *zone_table[];

static inline zone_t *page_zone(struct page *page)
{
        return zone_table[page->flags >> ZONE_SHIFT];
}

static inline void set_page_zone(struct page *page, unsigned long zone_num)
{
        page->flags &= ~(~0UL << ZONE_SHIFT);
        page->flags |= zone_num << ZONE_SHIFT;
}

然後來的內核(至今linux-4.7)中, 這些必要的標識(ZONE_DMA等)都是經過枚舉類型實現的(ZONE_DMA等用enum zone_type定義), 而後zone_table也被移除, 參照[PATCH] zone table removal miss merge

所以內核提供了新的思路, 參見include/linux/mm.h?v4.7, line 907

static inline struct zone *page_zone(const struct page *page)
{
    return &NODE_DATA(page_to_nid(page))->node_zones[page_zonenum(page)];
}

static inline void set_page_zone(struct page *page, enum zone_type zone)
{
    page->flags &= ~(ZONES_MASK << ZONES_PGSHIFT);
    page->flags |= (zone & ZONES_MASK) << ZONES_PGSHIFT;
}

static inline void set_page_node(struct page *page, unsigned long node)
{
    page->flags &= ~(NODES_MASK << NODES_PGSHIFT);
    page->flags |= (node & NODES_MASK) << NODES_PGSHIFT;
}

其中NODE_DATA使用了全局的node表進行索引.

在UMA結構的機器中, 只有一個node結點即contig_page_data, 此時NODE_DATA直接指向了全局的contig_page_data, 而與node的編號nid無關, 參照include/linux/mmzone.h?v=4.7, line 858, 其中全局惟一的cnode結點ontig_page_data定義在mm/nobootmem.c?v=4.7, line 27

#ifndef CONFIG_NEED_MULTIPLE_NODES
extern struct pglist_data contig_page_data;
#define NODE_DATA(nid)          (&contig_page_data)
#define NODE_MEM_MAP(nid)       mem_map
else
/*  ......  */
#endif

而對於NUMA結構的系統中, 全部的node都存儲在node_data數組中, NODE_DATA直接經過node編號索引便可, 參見NODE_DATA的定義

extern struct pglist_data *node_data[];
#define NODE_DATA(nid)          (node_data[(nid)])

那麼page的flags標識主要分爲4部分,其中標誌位flag向高位增加, 其他位字段向低位增加,中間存在空閒位

字段 描述
section 主要用於稀疏內存模型SPARSEMEM,可忽略
node NUMA節點號, 標識該page屬於哪個節點
zone 內存域標誌,標識該page屬於哪個zone
flag page的狀態標識

以下圖所示

3.2 內存頁標識pageflags

其中最後一個flag用於標識page的狀態, 這些狀態由枚舉常量enum pageflags定義, 定義在include/linux/page-flags.h?v=4.7, line 74. 經常使用的有以下狀態

enum pageflags {
        PG_locked,              /* Page is locked. Don't touch. */
        PG_error,
        PG_referenced,
        PG_uptodate,
        PG_dirty,
        PG_lru,
        PG_active,
        PG_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_swapcache,           /* Swap page: swp_entry_t in private */
        PG_mappedtodisk,        /* Has blocks allocated on-disk */
        PG_reclaim,             /* To be reclaimed asap */
        PG_swapbacked,          /* Page is backed by RAM/swap */
        PG_unevictable,         /* Page is "unevictable"  */
#ifdef CONFIG_MMU
        PG_mlocked,             /* Page is 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
        __NR_PAGEFLAGS,

        /* Filesystems */
        PG_checked = PG_owner_priv_1,

        /* 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,

        /* SLOB */
        PG_slob_free = PG_private,

        /* Compound pages. Stored in first tail page's flags */
        PG_double_map = PG_private_2,
};
頁面狀態 描述
PG_locked 指定了頁是否被鎖定, 若是該比特未被置位, 說明有使用者正在操做該page, 則內核的其餘部分不容許訪問該頁, 這能夠防止內存管理出現競態條件
PG_error 若是涉及該page的I/O操做發生了錯誤, 則該位被設置
PG_referenced 表示page剛剛被訪問過
PG_uptodate 表示page的數據已經與後備存儲器是同步的, 即頁的數據已經從塊設備讀取,且沒有出錯,數據是最新的
PG_dirty 與後備存儲器中的數據相比,該page的內容已經被修改. 出於性能能的考慮,頁並不在每次改變後當即回寫, 所以內核須要使用該標識來代表頁面中的數據已經改變, 應該在稍後刷出
PG_lru 表示該page處於LRU鏈表上, 這有助於實現頁面的回收和切換. 內核使用兩個最近最少使用(least recently used-LRU)鏈表來區別活動和不活動頁. 若是頁在其中一個鏈表中, 則該位被設置
PG_active page處於inactive LRU鏈表, PG_active和PG_referenced一塊兒控制該page的活躍程度,這在內存回收時將會很是有用
當位於LRU active_list鏈表上的頁面該位被設置, 並在頁面移除時清除該位, 它標記了頁面是否處於活動狀態
PG_slab 該page屬於slab分配器
PG_onwer_priv_1
PG_arch_1 直接從代碼中引用, PG_arch_1是一個體繫結構相關的頁面狀態位, 通常的代碼保證了在第一次禁圖頁面高速緩存時, 該位被清除. 這使得體系結構能夠延遲到頁面被某個進程映射後, 才能夠D-Cache刷盤
PG_reserved 設置該標誌,防止該page被交換到swap
PG_private 若是page中的private成員非空,則須要設置該標誌, 用於I/O的頁可以使用該字段將頁細分爲多核緩衝區
PG_private_2
PG_writeback page中的數據正在被回寫到後備存儲器
PG_head
PG_swapcache 表示該page處於swap cache中
PG_mappedtodisk 表示page中的數據在後備存儲器中有對應
PG_reclaim 表示該page要被回收。當PFRA決定要回收某個page後,須要設置該標誌
PG_swapbacked 該page的後備存儲器是swap
PG_unevictable 該page被鎖住,不能交換,並會出如今LRU_UNEVICTABLE鏈表中,它包括的幾種page:ramdisk或ramfs使用的頁, shm_locked、mlock鎖定的頁
PG_mlocked 該page在vma中被鎖定,通常是經過系統調用mlock()鎖定了一段內存
PG_uncached
PG_hwpoison
PG_young
PG_idle

內核中提供了一些標準宏,用來檢查、操做某些特定的比特位,這些宏定義在include/linux/page-flags.h?v=4.7, line 183

#define TESTPAGEFLAG(uname, lname, policy)
#define SETPAGEFLAG(uname, lname, policy)
#define CLEARPAGEFLAG(uname, lname, policy)

關於page flags的早期實現

  • linux-2.6之後的內核中, 不多出現直接用宏定義的標識, 這些標識大多經過enum枚舉常量來定義, 而後__NR_XXXX的形式結束, 正好能夠標記出宏參數的個數, 可是在早期的實現中, 這些變量都經過宏來標識

例如咱們的page->flags用enum pageflags來定義, 內存管理區類型經過zone_type來定義, 可是這些內容在早期的內核中都是經過宏定義來實現的.

形式以下

PageXXX(page):檢查page是否設置了PG_XXX位
SetPageXXX(page):設置page的PG_XXX位
ClearPageXXX(page):清除page的PG_XXX位
TestSetPageXXX(page):設置page的PG_XXX位,並返回原值
TestClearPageXXX(page):清除page的PG_XXX位,並返回原值

不少狀況下, 須要等待頁的狀態改變, 而後才能恢復工做. 所以內核提供了兩個輔助函數

http://lxr.free-electrons.com/source/include/linux/pagemap.h?v=4.7#L495
/*
 * Wait for a page to be unlocked.
 *
 * This must be called with the caller "holding" the page,
 * ie with increased "page->count" so that the page won't
 * go away during the wait..
 */
static inline void wait_on_page_locked(struct page *page)

// http://lxr.free-electrons.com/source/include/linux/pagemap.h?v=4.7#L504
/*
 * Wait for a page to complete writeback
 */
static inline void wait_on_page_writeback(struct page *page)

假定內核的一部分在等待一個被鎖定的頁面, 直至頁面被解鎖. wait_on_page_locked提供了該功能. 在頁面被鎖定的狀況下, 調用該函數, 內核將進入睡眠. 而在頁面解鎖後, 睡眠進程會被自動喚醒並繼續工做

wait_on_page_writeback的工做方式相似, 該函數會等待與頁面相關的全部待決回寫操做結束, 將頁面包含的數據同步到塊設備爲止.

4 全局頁面數組mem_map

mem_map是一個struct page的數組,管理着系統中全部的物理內存頁面。在系統啓動的過程當中,建立和分配mem_map的內存區域, mem_map定義在mm/page_alloc.c?v=4.7, line 6691

#ifndef CONFIG_NEED_MULTIPLE_NODES
/* use the per-pgdat data instead for discontigmem - mbligh */
unsigned long max_mapnr;
struct page *mem_map;

EXPORT_SYMBOL(max_mapnr);
EXPORT_SYMBOL(mem_map);
#endif

UMA體系結構中, free_area_init函數在系統惟一的struct node對象contig_page_data中node_mem_map成員賦值給全局的mem_map變量

相關文章
相關標籤/搜索