眼花的页结构体

页结构体(struct page)可以说是我见过的最为繁杂的结构体定义了,至今我只能说是略懂。然而这个结构体定义又是那么的重要,因为物理意义上的每个页面都对应一个结构体。可以说这个结构体是整个系统上所有内存的守护者。

每个细分成员的作用就不在本节展开了,这里主要是作为一个全局参考,从整体上对页结构体有个了解。具体的作用将在对应的章节中描述。

庞然大物

下面这个图已经是很老的状态了,自从有了folio后简化了不少。我还是放在这里,以后可以有个对照。看看folio简化的效果。

    struct page (include/linux/mm_types.h)

                         page
               +--------------------------------------------------------------+
               |flags                                                         |    page-flags-layout.h
               |   (unsigned long)                                            |    page-flags.h
   --+--       +==============================================================+
     |         |..............................................................|
     |         |page cache and anonymous pages                                |
     |         |    +---------------------------------------------------------+
     |         |    |lru                                                      |
     |         |    |    (struct list_head)                                   |
               |    |mapping                                                  |
               |    |    (struct address_space*)                              |
               |    |index                                                    |
               |    |    (pgoff_t)                                            |
  5 words      |    |private                                                  |
   union       |    |    (unsigned long)                                      |
               |    +---------------------------------------------------------+
               |..............................................................|
    has        |slab, slob, slub                                              |
               |    +---------------------------------------------------------+
  7 usage      |    |.........................................................|
               |    |                            .+---------------------------|
               |    |slab_list                   .|next                       |
               |    |    (struct list_head)      .|   (struct page*)          |
               |    |                            .|pages                      |
               |    |                            .|pobjects                   |
               |    |                            .|   (int)                   |
               |    |                            .+---------------------------|
               |    |.........................................................|
               |    +---------------------------------------------------------+
               |    |slab_cache                                               |
               |    |    (struct kmem_cache*)                                 |
               |    |freelist                                                 |
               |    |    (void*)                                              |
               |    +---------------------------------------------------------+
               |    |.........................................................|
               |    |s_mem    .counters          .+---------------------------|
               |    | (void*) . (unsigned long)  .|inuse                      |
               |    |         .                  .|objects                    |
               |    |         .                  .|frozen                     |
               |    |         .                  .|    (unsigned)             |
               |    |         .                  .+---------------------------|
               |    |.........................................................|
               |    +---------------------------------------------------------+
               |..............................................................|
               |Tail pages of compound page                                   |
               |    +---------------------------------------------------------+
               |    |compound_head                                            |
               |    |    (unsigned long)                                      |
               |    |compound_dtor                                            |
               |    |compound_order                                           |
               |    |    (unsigned char)                                      |
               |    |compound_mapcount                                        |
               |    |    (atomic_t)                                           |
               |    +---------------------------------------------------------+
               |..............................................................|
               |Second tail page of compound page                             |
               |    +---------------------------------------------------------+
               |    |_compound_pad_1                                          |
               |    |_compound_pad_2                                          |
               |    |    (unsigned long)                                      |
               |    |deferred_list                                            |
               |    |    (struct list_head)                                   |
               |    +---------------------------------------------------------+
               |..............................................................|
               |Page table pages                                              |
               |    +---------------------------------------------------------+
               |    |_pt_pad_1                                                |
               |    |    (unsigned long)                                      |
               |    |pmd_huge_pte                                             |
               |    |    (pgtable_t)                                          |
               |    |_pt_pad_2                                                |
               |    |    (unsigned long)                                      |
               |    |.........................................................|
               |    |pt_mm                         .pt_frag_refcount          |
               |    |    (struct mm_struct*)       .    (atomic_t)            |
               |    |.........................................................|
               |    |ptl                                                      |
               |    |    (spinlock_t/spinlock_t *)                            |
               |    +---------------------------------------------------------+
               |..............................................................|
               |ZONE_DEVICE pages                                             |
               |    +---------------------------------------------------------+
               |    |pgmap                                                    |
               |    |    (struct dev_pagemap*)                                |
               |    |hmm_data                                                 |
               |    |_zd_pad_1                                                |
     |         |    |    (unsigned long)                                      |
     |         |    +---------------------------------------------------------+
     |         |..............................................................|
     |         |rcu_head                                                      |
     |         |    (struct rcu_head)                                         |
     |         |..............................................................|
   --+--       +==============================================================+
     |         |..............................................................|
               |            .                 .                .              |
   4 bytes     |_mapcount   .page_type        .active          .units         |
    union      |  (atomic_t).   (unsigned int).  (unsigned int).   (int)      |
               |            .                 .                .              |
     |         |..............................................................|
   --+--       +==============================================================+
               |_refcount                                                     |
               |     (atomic_t)                                               |
               |mem_cgroup                                                    |
               |     (struct mem_cgroup)                                      |
               |virtual                                                       |
               |     (void *)                                                 |
               |_last_cpupid                                                  |
               |     (int)                                                    |
               +--------------------------------------------------------------+

page->flags

在页结构体众多成员中,flags字段在任何用途下都标示了对应页的属性。除此之外内核中还对这个字段做了非常巧(变)妙(态)的布局。

内核中有两个头文件和flags的定义有着密切的联系

* include/linux/page-flags-layout.h
* include/linux/page-flags.h

前者定义了该字段的布局,后者则定义了该字段具体的意义和相关操作的宏定义。

布局

以下对字段布局的描述摘自page-flags-layout.h

/*
 * page->flags layout:
 *
 * There are five possibilities for how page->flags get laid out.  The first
 * pair is for the normal case without sparsemem. The second pair is for
 * sparsemem when there is plenty of space for node and section information.
 * The last is when there is insufficient space in page->flags and a separate
 * lookup is necessary.
 *
 * No sparsemem or sparsemem vmemmap: |       NODE     | ZONE |             ... | FLAGS |
 *      " plus space for last_cpupid: |       NODE     | ZONE | LAST_CPUPID ... | FLAGS |
 * classic sparse with space for node:| SECTION | NODE | ZONE |             ... | FLAGS |
 *      " plus space for last_cpupid: | SECTION | NODE | ZONE | LAST_CPUPID ... | FLAGS |
 * classic sparse no space for node:  | SECTION |     ZONE    | ... | FLAGS |
 */

其中FLAGS部分的定义就在include/linux/page-flags.h文件中,这部分也是在内核代码中用来判断页属性的关键部分。

FLAGS定义

FLAGS是一个按照比特位来定义的属性集合,比如我们可以看一下定义从而大致了解一下这个FLAGS中都有哪些内容。

  enum pageflags {
  	PG_locked,		/* Page is locked. Don't touch. */
  	PG_referenced,
  	PG_uptodate,
  	PG_dirty,
  	PG_lru,
  	PG_active,
  	PG_workingset,
    ...
	__NR_PAGEFLAGS,
    ...
  }

整个FLAGS的长度为__NR_PAGEFLAGS,并且内核定义又了NR_PAGEFLAGS。这两者的值一致。

并且在page-flags-layout.h中,用下面的代码保证了这些神秘的东西加起来不会放不下。

#if ZONES_WIDTH + SECTIONS_WIDTH + NODES_WIDTH + KASAN_TAG_WIDTH + LAST_CPUPID_WIDTH \
	> BITS_PER_LONG - NR_PAGEFLAGS
#error "Not enough bits in page flags"
#endif

FLAGS操作

有了定义,接下来就是如何操作。这里就是体现C语言博大精深的时刻了,其中对字段操作的宏定义总结如下:

* PageXXX()
* SetPageXXX()
* ClearPageXXX()

这三个宏分别用来判断、设置、清除page->flags相应的属性。在内核代码中使用非常广泛,以后看到长成这样的,就可以到这个头文件中搜索。

定义FLAGS操作

虽然我们上面看到了,在代码中用的是SetPageXXX()/ClearPageXXX()来操作flags,但是因为这一套基本是同样的操作,所以代码中并没有给每个bit定义单独的Set/Clear。

而是通过两个宏来定义的(还有folio相关的操作,放在后面讲)

* PAGEFLAG   原子操作
* __PAGEFLAG 非原子操作

所以在page-flags.h文件中可以看到一串用PAGEFLAG()定义的内容。如

    PAGEFLAG(Dirty, dirty, PF_HEAD)
    PAGEFLAG(LRU, lru, PF_HEAD)
    PAGEFLAG(Active, active, PF_HEAD)

而PAGEFLAG的定义是:

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

所以相当于是一套都定义好了,往下具体的实现是用test_bit/set_bit/clear_bit来做的。

另外值的注意的是,其中[TEST|SET|CLEAR]PAGEFLAG的定义都包含了对应FOLIO的定义。

policy

在PAGEFLAG()定义中,有第三个参数policy。我们来看看它究竟是什么。

首先看看这个policy是怎么用的。

static __always_inline int Page##uname(const struct page *page)		\
{ return test_bit(PG_##lname, &policy(page, 0)->flags); }


static __always_inline void ClearPage##uname(struct page *page)		\
{ clear_bit(PG_##lname, &policy(page, 1)->flags); }

那这个policy究竟是什么呢? 哦,是一大串宏定义:

#define PF_POISONED_CHECK(page) ({					\
		VM_BUG_ON_PGFLAGS(PagePoisoned(page), page);		\
		page; })
#define PF_ANY(page, enforce)	PF_POISONED_CHECK(page)
#define PF_HEAD(page, enforce)	PF_POISONED_CHECK(compound_head(page))
#define PF_NO_TAIL(page, enforce) ({					\
		VM_BUG_ON_PGFLAGS(enforce && PageTail(page), page);	\
		PF_POISONED_CHECK(compound_head(page)); })
#define PF_NO_COMPOUND(page, enforce) ({				\
		VM_BUG_ON_PGFLAGS(enforce && PageCompound(page), page);	\
		PF_POISONED_CHECK(page); })
#define PF_SECOND(page, enforce) ({					\
		VM_BUG_ON_PGFLAGS(!PageHead(page), page);		\
		PF_POISONED_CHECK(&page[1]); })

仔细看了一下,这个policy还有两个作用:

  • 检查传入的page是否符合要求

  • 将page转换为想要的对象

其中第二点在PF_HEAD/PF_NO_TAIL/PF_SECOND中会处理。这样最后检测/设置的flags就是对应head或者page[1]的flags,而不是传入的page的。

而作为检测手段,当第二个参数enforce为0的时候,某些check就不起作用了。而搜了一圈,只有TESTPAGEFLAG时,才会是0。

再详细解释一下其中几个定义:

  • PF_HEAD: 并没有检测,直接取了compound_head()返回,也不知道是何原因。不过使用它的几个flags也却是只能在head page。

  • PF_NO_TAIL: 和PF_HEAD一样,最后返回的也是head page。但区别在于PF_NO_TAIL会验证是否是head page。

  • PF_SECOND: 传入的page一定是head page,出来的是&page[1]。

PS: 最终这些PAGEFLAG,都要被替换成FOLIO_FLAG。

PageType

前面我们提到内核中SetPageXXX()的操作都是由一套宏定义操作page->flags实现的。

现在发现还有一套非常类似的操作,__SetPageXXX(),是操作page->page_type实现的.

PAGE_TYPE_OPS()
FOLIO_TYPE_OPS()

目前一共定义了这些type,以后看到PageXXX()这种宏定义,还记得看看是不是page_type。

enum pagetype {
	/* 0x00-0x7f are positive numbers, ie mapcount */
	/* Reserve 0x80-0xef for mapcount overflow. */
	PGTY_buddy		= 0xf0,
	PGTY_offline		= 0xf1,
	PGTY_table		= 0xf2,
	PGTY_guard		= 0xf3,
	PGTY_hugetlb		= 0xf4,
	PGTY_slab		= 0xf5,
	PGTY_zsmalloc		= 0xf6,
	PGTY_unaccepted		= 0xf7,
	PGTY_large_kmalloc	= 0xf8,

	PGTY_mapcount_underflow = 0xff
};

Last updated

Was this helpful?