LRUリスト


ページフレームは先頭から16MBまでをZONE_DMA、16MBから896MBまでをZONE_NORMAL、896MB以降をZONE_HIGHMEMとして、その間のページフレームを回収処理のための管理をしています。ゾーンはstruct pglist_dataとする静的変数下のメンバー、struct zone node_zones[MAX_NR_ZONES]で管理されています。

ZONE_HIGHMEMはカーネルがストレートマップできない故ですが、ZONE_DMAとZONE_NORMALについては別段の意味はないようです。ただはDMA回路で16MBまでしか使うことのできないものがあるらしく、そのため通常メモリーを使う時は、ZONE_NORMALはDMAに予約しておいて、できるだけZONE_NORMALから使いましょう。ということのようで、実際ZONE_DMAからページを確保しても、ZONE_NORMALから取得したものと以降の処理はまったく同じです。(たぶん)

そしてその各ゾーンには、struct list_head active_list/inactive_listメンバーを有して、回収対象となるページフレームをactive_list/inactive_listのどちらかに、リストノードをpage->lruとしてリストしていきます。この2つのリストをLRUリストと言います。当然ページ回収ではinactive_listから行われます。

ページキャッシュ等で新規に獲得されたページはinactive_listに繋がれます。そしてそのページが参照されるとactive_listリストにつなぎ返られるのですが、実装ではpageフラグにPG_referencedフラグを有していて、そこのビットがセットされていなければ、その時の参照ではそのビットをセットするだけです。もしそのビットがセットされていて初めてinactive_listからactive_listに繋ぎ返られます。両リスト2段階の参照度を有しているということです。

上記の処理を行うのがmark_page_accessed関数です。ファイル読み込み処理のdo_generic_file_read関数から呼ばれたりしています。ページキャッシュを参照するからです。

PageActive/PageReferenced/PageLRU/PageReferencedマクロは、すべてpage->flagのビットをチェックするものです。PageActiveマクロはPG_activeビットのチェックを行います。このビットがセットされていないと、page->lruはinactive_listリストに繋がれている事を意味します。そしてPageReferencedマクロでPG_referencdビットがセットされているとactive_listリストに繋ぎ換えです。なおPageLRUはこのページがLRUリストに繋がっているか(回収対象のページ)どうかのチェックです。カーネルが使っているページとか、ramfsのように回収できないページもあるからです。

繋ぎ換えの場合、activate_page関数へ、そしてPG_referencdビットをリセットします。繋ぎ換えが不要の場合、もしPG_referencdビットがリセットならセットするだけです。もしPageActive(page) && PageReferenced(page)なら変化なしです。
void mark_page_accessed(struct page *page)
{
       if (!PageActive(page) && PageReferenced(page) && PageLRU(page)) {
               activate_page(page);
               ClearPageReferenced(page);
       } else if (!PageReferenced(page)) {
               SetPageReferenced(page);
       }
}
activate_page関数で繋ぎ換えます。if (PageLRU(page) && !PageActive(page)) でページがinactive_listに繋がれていることを確認します。そしてdel_page_from_inactive_list関数で、ページの属するzoneのinactive_listから削除して、PG_activeビットをセットした後、add_page_to_active_list関数でactive_listに追加します。

__count_vm_event関数はCPU変数下のvm_event_statesメンバーの配列のインデックスPGACTIVATEをインクリメントしているだけです。たぶん統計情報だと思います。
void activate_page(struct page *page)
{
       struct zone *zone = page_zone(page);

       spin_lock_irq(&zone->lru_lock);
       if (PageLRU(page) && !PageActive(page)) {
               del_page_from_inactive_list(zone, page);
               SetPageActive(page);
               add_page_to_active_list(zone, page);
               __count_vm_event(PGACTIVATE);
               mem_cgroup_move_lists(page, true);
       }
       spin_unlock_irq(&zone->lru_lock);
}

static inline void
add_page_to_active_list(struct zone *zone, struct page *page)
{
       list_add(&page->lru, &zone->active_list);
       __inc_zone_state(zone, NR_ACTIVE);
}

static inline void
del_page_from_inactive_list(struct zone *zone, struct page *page)
{
       list_del(&page->lru);
       __dec_zone_state(zone, NR_INACTIVE);
}

補足

list_delで削除する場合、繋がっているzone->inactive_listの引数は必要ありません。双方向リストでは、page->lruは次のページと前のページが繋がっているだけで、削除は次のページのprevを前のページに、前のページのnextを次にページにすればいいからです。

最終更新 2011/04/16 16:01:43 - north
(2011/04/16 03:20:44 作成)


検索

アクセス数
3585580
最近のコメント
コアダンプファイル - sakaia
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
Adsense
広告情報が設定されていません。