向struct page里塞更多东西

翻译并篡改更新自LWN.NET 2013年8月的一篇文章。

通常,内核开发人员更倾向于设计可读性和可维护性高的数据结构。当人们理解一段代码使用的数据结构时,对代码本身的理解通常就不会遥远。因此,内核使用最频繁的数据结构之一也是其可理解性最差的之一,这个事实可能会令人惊讶。这个数据结构就是struct page,代表物理内存的页面。最近的一个补丁集,使struct page更加复杂,当然,这也为快速概述如何使用此结构提供了契机。

在大多数Linux系统上,一页物理内存包含4096个字节,这意味着典型的系统包含数百万个页面。维护每一个页面都需要一个struct page,这给struct page带来了巨大压力,因为在struct page中每增加1个byte,大概都会提高上兆字节的内存使用量。这就造成了一种情况,即如果可以避免使struct page变大,几乎所有的技巧都是合理的。

Enter Joonsoo Kim发布了一个补丁集,旨在将更多信息压缩到struct page中,但不增加其大小。特别是,他担心struct slab占用的空间,该空间由slab内存分配器使用(可以在内核中配置的三个分配器之一,其他分配器称为SLUB和SLOB)。关于slab的我就不翻译了,相关的slab单元也大量使用。现在作为编辑我的系统上的/proc/slabinfo仅显示ext4 inode高速缓存的活动slab单元就超过28,000个。减少空间使用将是值得欢迎的; Joonsoo认为可以做到这一点-通过将struct slab的内容折叠到表示包含slab本身的内存的页面结构中。

struct page中有什么

逐步浏览struct page并注意为容纳额外数据而进行的更改,可能是最好地了解Joonsoo的补丁的办法。出于好奇,可以在<linux / mm_types.h>中找到此结构的完整定义。第一个 字段是unsigned long flags,这段区域描述page的状态:脏,锁定,正在写回等。但是,实际上,这个领域并不像看起来那样简单。甚至内核是否已用完页面标志空间的问题也很难回答。有关如何使用标志字段的一些详细信息,在09年一篇文章有所描述(译:有点久,最近我还写了一篇关于flags解读的文章)。 

在flag之后是struct address_space* mapping段,对于用于页面高速缓存的page,(在大多数系统上,页面的很大一部分),mapping指向使用当前page的file。但是,如果该页面是一个匿名页面(由swap支持的用户空间内存),则mapping将指向anon_vma结构,从而使内核可以快速找到引用该页面的页表;有关图表和详细信息,请参见本文。为了避免这两种类型的页面之间的混淆,匿名页面将在mapping中最低有效位置位,指针本身始终与至少一个word对齐,因此该位应该是0的。

这是Joonsoo的补丁程序进行更改的第一个地方。mapping字段当前不用于内核空间内存,因此他可以将其用作指向slab中第一个空闲对象的指针,从而无需将其保留在struct slab中。

下面的区域就开始变得更复杂了(译:这篇文章有点古老,在kernel4.15 struct page已经演变成其他样子,各字段定义已经变化,不过还好文中描述的字段含义没有变),下面这个联合体中:

union {

pgoff_t index; /* Our offset within mapping. */

void *freelist; /* sl[aou]b first free object */

};

index字段用于page-cache页面来持有相关file的offset值,但如果页面是归slub、slob管理的,freelist则指向空闲对象的列表,slab并不使用freelist,但是Joonsoo的补丁使slab也如slub、slob一样使用freelist。上面这些是第一个双word块内存区域。

第二个双word块的定义在下面这段联合体,counters和匿名的struct本来都是slub分配器用的,units则是slob用的,_mapcount和_refcount段都是用于引用计数的,_mapcount是统计PTE指向这个page的数量,而_refcount(改自_count)则用于使用此页面的计数;_mapcount还辅助管理合成页面。而active段则是Joonsoo添加的了,用于统计slab活跃的对象,原先在struct slab中。

union {

unsigned long counters;

struct {

    union {

        atomic_t _mapcount;

        unsigned int active; /* SLAB */

        struct { /* SLUB */

            unsigned inuse:16;

            unsigned objects:15;

            unsigned frozen:1;};

        int units; /* SLOB */};

    atomic_t _refcount;};

};

第三个双word块的定义如下,对于匿名和页面缓存页面,lru指向本页面所在的一个最不常用的列表中的位置。SLUB使用匿名结构,而SLOB使用list。译:pgmap是在更新版本内核添加的,ZONE_DEVICE页面从不在lru上或由slab分配器处理,这指向host设备页面映射,是外设内存。在这个块里,Joonsoo的补丁在union中添加了rcu_head,用于相关slab使用RCU机制释放内存。

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; 

    struct { /* slub per cpu partial pages */

        struct page *next; /* Next partial slab */

        int pages; /* Nr of partial slabs left */

        int pobjects; /* Approximate # of objects */ };

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

        unsigned int compound_dtor;

        unsigned int compound_order;

    };

    #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

}

下段的private字段本质上属于已分配页面的内核子系统。它在整个内核中看到了许多用途。特别是文件系统,大量使用它。如果内核使用这个page来保存页表,则使用ptl字段;如果CPU数量支持,它允许将页表锁分解成多个锁。在大多数配置里,一个系统包含4个或更多个处理器将会按照这种方式拆分锁。slab_cache用作slab和SLUB的指向其kmem_cache。

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

spinlock_t *ptl;

struct kmem_cache *slab_cache; /* SL[AU]B: Pointer to slab */

};

之后就是cgroup的struct mem_cgroup *mem_cgroup;

基于上面描述的Joonsoo的补丁程序,以前保存在struct slab中的许多信息被移到了struct page中,struct slab中的相应字段也可以删除了,从结构体上讲并不大,但是对于运行系统中的成千上万个slab来讲,内存的节省是很明显的。将活动集中在struct page上也可能具有有益的cache效果,从而总体上提高了性能。因此,即使以复杂的情况为代价,这些补丁也很值得。

而且情况确实很复杂:struct page是一个复杂的结构,其中包含许多有关其使用的细微规则。节省带来的问题就是它使用得太多了,以至于对规则的任何误解都会迅速导致严重的问题。尽管如此,尝试将更多信息放入此结构中并不是胆小的人的任务。Joonsoo是否会成功还有待观察,但他显然不是第一个将struct page作为存储一些有用的内存管理信息的场所的人。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值