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)なら変化なしです。
__count_vm_event関数はCPU変数下のvm_event_statesメンバーの配列のインデックスPGACTIVATEをインクリメントしているだけです。たぶん統計情報だと思います。
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); }