Linux記憶體描述之記憶體頁面page--Linux記憶體管理(四)

来源:https://www.cnblogs.com/linhaostudy/archive/2018/11/24/10013739.html
-Advertisement-
Play Games

1 Linux如何描述物理記憶體 Linux把物理記憶體劃分為三個層次來管理 層次| 描述 | 存儲節點(Node) | CPU被劃分為多個節點(node), 記憶體則被分簇, 每個CPU對應一個本地物理記憶體, 即一個CPU node對應一個記憶體簇bank,即每個記憶體簇被認為是一個節點 管理區(Zone) ...


1 Linux如何描述物理記憶體

Linux把物理記憶體劃分為三個層次來管理

層次 描述
存儲節點(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

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

分頁單元把所有RAM分為固定長度的頁幀(也叫頁框, 物理頁, 英文page frame). 每一個頁幀包含一個頁(page). 也就是說一個頁幀的長度與一個頁的長度一致. 頁框是主存的一部分, 因此也是一個存儲區域. 簡單來說, 頁是一個數據塊, 可以存放在任何頁框(記憶體中)或者磁碟(被交換至交換分區)中

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

2 頁幀

內核把物理頁作為記憶體管理的基本單位. 儘管處理器的最小可定址單位通常是字, 但是, 記憶體管理單元MMU通常以頁為單位進行處理. 因此,從虛擬記憶體的上來看,頁就是最小單位.

頁幀代表了系統記憶體的最小單位, 對記憶體中的每個頁都會創建struct page的一個實例. 內核必須要保證page結構體足夠的小,否則僅struct page就要占用大量的記憶體.

因為即使在中等程式的記憶體配置下, 系統的記憶體同樣會分解為大量的頁. 例如, IA-32系統中標準頁長度為4KB, 在記憶體大小為384MB時, 大約有100000頁. 就當今的標準而言, 這個容量算不上很大, 但頁的數目已經非常可觀了

因而出於節省記憶體的考慮,內核要儘力保持struct page儘可能的小. 在典型的系統中, 由於頁的數目巨大, 因此對page結構的小改動, 也可能導致保存所有page實例所需的物理記憶體暴漲.

頁的廣泛使用, 增加了保持結構長度的難度 : 記憶體管理的許多部分都使用頁, 用於各種不同的用途. 內核的一部分可能完全依賴於struct page提供的特定信息, 而這部分信息堆內核的其他部分頁可能是完全無用的. 等等.

2.1 struct page結構

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

出於節省記憶體的考慮,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變數


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、前面兩篇文章分別介紹了定義泛型類型、泛型委托、泛型介面以及聲明泛型方法: 詳解C#泛型(一) 詳解C#泛型(二) 首先回顧下如何構建泛型類: 其中,尖括弧<>中的T代表的是該泛型類的類型參數,在使用時可以指定其類型,例如,指定類型參數為整數類型,創建封閉式構造類MyClass<int>: 二、這 ...
  • 下載地址:https://ckeditor.com/ckeditor-4/download/ 一、使用方法: 1、在頁面中引入ckeditor核心文件ckeditor.js 2、在使用編輯器的地方插入HTML控制項<textarea> <textarea id="Contents" class="la ...
  • 1】測試工具jmeter環境安裝 1.1】安裝jdk環境 1:必須安裝jdk8.0(我嘗試安裝最新版本不行,好像當時沒有配置好。之後安裝8.0遍可以正常運行) 下載地址:單擊此段 配置jdk環境:滑鼠右鍵點擊桌面電腦→進入屬性→點擊高級系統設置→高級→環境變數 2:系統變數→新建 JAVA_HOM ...
  • DevExpress中的Dashboard這個圖形插件真的很好用, 只需要在設計器綁定數據就行了,完全不用寫後臺代碼.我也是剛學這個插件,可能還有一些我沒有瞭解的知識點,忘各位大佬不吝賜教.我寫這篇博文也是為了給想我一樣剛研究這個插件的博友們一些我的總結,給各位節省一些時間,哈哈. 1. 當我們創建 ...
  • 門禁服務程式已經調試完成,基於項目實時性要求,使用SignalR實現門禁狀態實時獲取和控制。 ...
  • 接著上一章繼續聊這個項目 本章主要會介紹到一下幾點 配置文件強類型model轉化 redis使用 一.基礎類介面的實現 1.首先創建IConfigGeter介面 介面代碼如下: 2.實現介面 介面實現代碼如下 3.創建一個ConfigLocator配置文件獲取器 實現代碼如下: 二.在program ...
  • 今天給大家分享一下採用asp.net core 快速構建小型創業公司後臺管理系統,該項目是我給一個朋友做的,將要用到公司項目,今天分享出來許可權管理模塊喜歡的朋友可以試用用一下。 項目不是一個什麼新項目,也沒有用到什麼牛逼的東西,但裡面融入了我用asp.net core構建管理系統的思考,可以說是思想 ...
  • 1. 內核空間和用戶空間 過去,CPU的地址匯流排只有32位, 32的地址匯流排無論是從邏輯上還是從物理上都只能描述4G的地址空間(232=4Gbit),在物理上理論上最多擁有4G記憶體(除了IO地址空間,實際記憶體容量小於4G),邏輯空間也只能描述4G的線性地址空間。 為了合理的利用邏輯4G空間,Linu ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...