カーネルは4.1系です。
include/linux/mm_inline.hにLRUへの登録・削除処理の実装があります。
static __always_inline void add_page_to_lru_list(struct page *page, struct lruvec *lruvec, enum lru_list lru) { int nr_pages = hpage_nr_pages(page); mem_cgroup_update_lru_size(lruvec, lru, nr_pages); list_add(&page->lru, &lruvec->lists[lru]); __mod_zone_page_state(lruvec_zone(lruvec), NR_LRU_BASE + lru, nr_pages); } static __always_inline void del_page_from_lru_list(struct page *page, struct lruvec *lruvec, enum lru_list lru) { int nr_pages = hpage_nr_pages(page); mem_cgroup_update_lru_size(lruvec, lru, -nr_pages); list_del(&page->lru); __mod_zone_page_state(lruvec_zone(lruvec), NR_LRU_BASE + lru, -nr_pages); }
LRUに関連する構造体はこれらです。
これらの構造体の関連はこのようになります。
LRUはadd_page_to_lru_list()見ての通りlruvec構造体のlists配列のどこかにつながる感じです。
page構造体からlruvec構造体はmem_cgroup_page_lruvec()を使うことで取得できます。
lruvec = mem_cgroup_page_lruvec(page, page_zone(page));
mem_cgroup_page_lruvec()の処理のうち、最初にmemcgを使わない場合の処理がありますが、ここではmemcgは有効の場合を見てるので飛ばして、本筋的なところはこの辺です。
memcg = page->mem_cgroup; /* * Swapcache readahead pages are added to the LRU - and * possibly migrated - before they are charged. */ if (!memcg) memcg = root_mem_cgroup; mz = mem_cgroup_page_zoneinfo(memcg, page); lruvec = &mz->lruvec;
page構造体の構造体のメンバ変数mem_cgroupがpageが所属するmemcgのmem_cgroup構造体にアクセスできます。そしてmem_cgroup_page_zoneinfo()を使ってmem_cgroup_per_zone構造体を取得します。mem_cgroup_page_zoneinfo()はこんな関数です。
static struct mem_cgroup_per_zone * mem_cgroup_page_zoneinfo(struct mem_cgroup *memcg, struct page *page) { int nid = page_to_nid(page); int zid = page_zonenum(page); return &memcg->nodeinfo[nid]->zoneinfo[zid]; }
nidがNUMAのノード番号でzidがZONE_NORMALとかのインデックスですね(きっと)。page構造体からそのmemcg使っているNUMAノードとZONEを取得してるはずです。これでlruvec構造体は取得できました。
では、実際にadd_page_to_lru_list()を呼んでるところを見てみると、例えばこのunlock_page_lru()があります。
static void unlock_page_lru(struct page *page, int isolated) { struct zone *zone = page_zone(page); if (isolated) { struct lruvec *lruvec; lruvec = mem_cgroup_page_lruvec(page, zone); VM_BUG_ON_PAGE(PageLRU(page), page); SetPageLRU(page); add_page_to_lru_list(page, lruvec, page_lru(page)); } spin_unlock_irq(&zone->lru_lock); }
この関数だと対象のzoneはpage構造体から取得しています。そして、mem_cgroup_page_lruvec()でlruvec構造体を取得してからのadd_page_to_lru_list()でlruvecのlistsのリストにpageを繋いでいます。listsのインデックスはpage_lru()で決めていますね。
static inline enum lru_list page_lru_base_type(struct page *page) { if (page_is_file_cache(page)) return LRU_INACTIVE_FILE; return LRU_INACTIVE_ANON; }
ファイルキャッシュとして使うかどうかでindexが変わるようです。
というわけで、LRUはmemcg単位に存在していて、NUMAノードやZONEによって違うlruvecが使われます。そして、pageの用途によっても違うリストに繋がるというのがわかりました。 ( ´ー`)フゥー...
新装改訂版 Linuxのブートプロセスをみる (アスキー書籍)
- 作者: 白崎博生
- 出版社/メーカー: KADOKAWA / アスキー・メディアワークス
- 発売日: 2014/10/02
- メディア: Kindle版
- この商品を含むブログ (2件) を見る