上次介绍了物理内存管理中三位主要人物中的node 和zone。这两位是当官的,一个是县长,一个是里长,不敢不先介绍啊。接下来出场的就是我们的老百姓了 --- page frame。

Page frame是物理内存的基本组成单位,在Kernel中由结构体 struct page 来描述。

struct page {
    unsigned long flags;
    
    atomic_t _count;
    
    union {
        atomic_t _mapcount;
        unsigned int inuse;
    };
    
    union {
        struct {
            unsigned long private;
            struct address_space *mapping; 
        };
        struct kmem_cache *slab;    /* SLUB: Pointer to slab */
        struct page *first_page;    /* Compound tail pages */
    };
    
    union {
        pgoff_t index;      /* Our offset within mapping. */
        void *freelist;     /* SLUB: freelist req. slab lock */
    };
    
    struct list_head lru;      
};

这个结构体是不是长得很有特色?它里面的'union'特别多。

在中国,有好多大家不喜欢的制度,归根到底就一个原因 --- 人多。所以才有了户口制度来限制城市人口,所以才会采用高考制度来筛选人才。。。

在内存管理中,我们也面临着相同的问题 --- 内存页太多。假设我们有1G内存,采用4K的分页,则会有262144 (26万)个内存页,相应的就需要26万个struct page结构体。如果该结构体的大小增加一点点,放大26万倍,增加的内存消耗就很可观了。

为此,Kernel是绞尽了脑汁来减少该结构体的大小。毕竟全国百姓那么多,每个人多分配点田地,那整体的田地消耗太大。不像县长或里长这些当官的,数量有限,每个人多贪污点也影响不大。

Kernel采取的措施就是尽可能地重用同一个成员变量。房子大小有限,只能是白天当书房,晚上当卧室。

这就是为啥这个结构体中大量的使用了union。

  • flags: 用来描述page frame的属性或状态。

  • _count:这个没啥好说的,使用计数器。

  • _mapcount:也是个计数器,不过这里只是统计页表中有多少页表项指向该page frame。

  • inuse, slab, freelist:由slub使用,暂且略去不讲。

  • private:这个。。不同的使用场景下表示的含义也不同。为了节省空间而重用成员变量的另一种形式。是攻还是受,得看遇到了谁。

  • mapping:这个也是个多重人格,攻受兼备的主。一般会和 index 配合,表示在page cache中该page frame属于哪个address space。以后会详细讲到。

  • first_page: 在compound page中指向带头大哥。

  • lru: 还记不记得在结构体zone中有两个链表:active_list, inactive_list。嗯,lru就是干这个用的。


百闻不如一见。我们拿一个具体的例子,看看这三位人物的主要属性是怎么赋值的。

该实例中物理内存被分为两个node。

struct pglist_data {
  node_zones = ffff810000008101,
  node_zonelists = ffff81000000ad01,
  nr_zones = 3,
  node_mem_map = 0xffff8117df000000,
  
  node_start_pfn = 0,
  node_present_pages = 25162223,
  node_spanned_pages = 25427968,
  
  node_id = 0,
  kswapd_wait = ffff81000000d619,
  kswapd = 0xffff8117a27e3300,
  kswapd_max_order = 0
}


struct pglist_data {
  node_zones = ffff811840000000,
  node_zonelists = ffff811840002c00,
  nr_zones = 3,
  node_mem_map = 0xffff812fe0000000,
  
  node_start_pfn = 25427968,
  node_present_pages = 25165824,
  node_spanned_pages = 25165824,
  
  node_id = 1,
  kswapd_wait = ffff811840005518,
  kswapd = 0xffff8117a27e3300,
  kswapd_max_order = 0
}


我们以第一个Node为例,它被分成了三个zones:DMA, DMA32 and Normal。

  node_zones = {{
      pages_min = 0,
      pages_low = 0,
      pages_high = 0,
      lowmem_reserve = {0, 2978, 96722, 96722},
      node = 0,
      free_area =
      
      active_list = {
        next = 0xffff810000008a91,
        prev = 0xffff810000008a91
      },
      inactive_list = {
        next = 0xffff810000008aa1,
        prev = 0xffff810000008aa1
      },
      nr_scan_active = 0,
      nr_scan_inactive = 0,
      pages_scanned = 0,
      prev_priority = 12,
      
      wait_table = 0xffff810000001000,
      wait_table_hash_nr_entries = 16,
      wait_table_bits = 4,
      zone_pgdat = 0xffff810000008101,
      zone_start_pfn = 0,
      spanned_pages = 4096,
      present_pages = 1233,
      name = 0xffffffff8058c55c "DMA"
    }, {
      pages_min = 216,
      pages_low = 270,
      pages_high = 324,
      lowmem_reserve = {0, 0, 93744, 93744},
      node = 0,
      free_area = 
      
      active_list = {
        next = 0xffff810000009591,
        prev = 0xffff810000009591
      },
      inactive_list = {
        next = 0xffff8100000095a1,
        prev = 0xffff8100000095a1
      },
      nr_scan_active = 0,
      nr_scan_inactive = 0,
      pages_scanned = 0,
      prev_priority = 12,
      
      wait_table = 0xffff810001000000,
      wait_table_hash_nr_entries = 4096,
      wait_table_bits = 12,
      zone_pgdat = 0xffff810000008101,
      zone_start_pfn = 4096,
      spanned_pages = 1044480,
      present_pages = 762517,
      name = 0xffffffff8056824d "DMA32"
    }, {
      pages_min = 6819,
      pages_low = 8523,
      pages_high = 10228,
      lowmem_reserve = {0, 0, 0, 0},
      node = 0,
      free_area =
      
      active_list = {
        next = 0xffff81180da37e68,
        prev = 0xffff81183d80b528
      },
      inactive_list = {
        next = 0xffff81180f4e6be8,
        prev = 0xffff81183d823b68
      },
      nr_scan_active = 0,
      nr_scan_inactive = 0,
      pages_scanned = 0,
      prev_priority = 12,
      
      wait_table = 0xffff810001020000,
      wait_table_hash_nr_entries = 4096,
      wait_table_bits = 12,
      zone_pgdat = 0xffff810000008101,
      zone_start_pfn = 1048576,
      spanned_pages = 24379392,
      present_pages = 23998464,
      name = 0xffffffff80568253 "Normal"
}


至此物理内存管理中三位主要人物就介绍完了。他们之间都有些什么恩怨情仇血雨腥风的故事呢?敬请期待!