メモリ確保アルゴリズム詳細

下記do_try_to_free_pages関数は、ユーザメモリ、バッファキャッシュなどをを検索し、利用頻度度の低いものからどんどん解放する。目的の解放数が達成できるまでの間、何度も繰り返す。

解放するとはいっても、v2.2の時とは異なりv2.4では、好きなときにいつでも解放可能なキャッシュ(INACTIVE-CLEANリストにリンクされたページ)まで落すのみで、完全なフリーページにすることはない。(そのページ上のデータと全く同じものがディスク上にも存在する状態であるため、いつそのページが別の用途に転用されても問題が生じない)

ページ解放は二段階で行われる。

  1. 解放候補のページ(INACTIVE_DIRTYリスト上のページ)の解放を 試みる。(page_launder関数)
  2. 現在利用中のページ(ACTIVEリスト上のページ)のうち、参照頻度の 低いものを選び出し、解放候補のページ(INACTIVE_DIRTYリスト 上のページ)、解放可能なページ(INACTIVE_CLEANリスト上のページ) とする。必要であればSWAPデバイス上にページ上のデータを吐き出す。 (refill_inactive関数)
do_try_to_free_pages()
        if(空きメモリが不足) {
                 解放候補のページ(INACTIVE_DIRTYリスト上のページ)を
                   いつでも解放可能なページ(INACIVE_CLEANリスト上の
                   ページにする(page_launder関数)
        }
        if(空きメモリが不足 || 解放候補のページが少なめ) {
                ディレクトリエントリキャッシュの解放(shrink_dcache_memory関数)
                iノードキャッシュの解放(shrink_icache_memory関数)
                利用中(ACTIVE)のメモリの解放を試みる(refill_inactive関数)
        } else {
                カーネル内メモリアロケータ内の解放可能な
                    メモリを解放する(kmem_cache_reap関数)
        }

page_lander関数はその関数名の示すとおり、解放候補のページの洗濯をし、いつでも解放可能な綺麗な状態にする。いつでも解放可能な状態にするだけで、本当のフリーページに戻すようなことはしない。

page_launder()
        for(解放候補リストinactive_dirty) {
                if (最近参照されたページ) {
                        ページをACTIVE状態にする(active_listに繋ぐ)
                        continue
                }
                if (I/O中なら) {
                        inactive_dirtyリストに繋ぎ直す
                        continue
                }
                if (バッファ域、バッファ域にマップされたページキャッシュ) {
                        バッファ域の解放(try_to_free_buffers関数)
                        if(解放できなかったなら) {
                                (I/O中の場合、まだDirtyな場合など)
                                inactive_dirtyリストに繋ぎ直す
                        } else if(複数のところから参照されている)
                                ACTIVE状態にする(active_listに繋ぐ)
                        else /* ページキャッシュなら */
                                inactive_cleanリストに繋ぐ
                } else if (ページキャッシュ) {
                        inactive_cleanリストに繋ぐ
                } else {
                        ACTIVE状態にする(active_listに繋ぐ)
                }
                if(最初なら) {
                        バッファのフラッシュ(wakeup_bdflush関数)
                }
        }

解放候補とできるページ量が少なくなってくるとrefill_inactive関数にて、解放候補のページを作り出す。

プロセスページのスワップへの追い出し(swap_out関数)より、キャッシュ域の解放(refill_inactive_scan関数)を優先的に行う。

refill_inactive()
        カーネル内メモリアロケータ内の解放可能な
              メモリを解放する(kmem_cache_reap関数)

        for(プライオリティを変更しつつ) {
                /*swapアウトによる空きメモリの生成*/
                while (refill_inactive_scan() || swap_out()) {
                       if(目的のページ数を解放したら) return
                }
                ディレクトリエントリキャッシュの解放(shrink_dcache_memory関数)
                iノードキャッシュの解放(shrink_icache_memory関数)

                /*共有メモリのスワップアウト*/
                while (shm_swap()) {
                       if(目的のページ数を解放したら) return
                }
                if(空きメモリ量が十分になった) return
        }

ページが最近参照されたかどうかを示すためにpage構造体中に参照ビット(PG_referenced)を持っており、これらキャッシュを参照する度(find_page_nolock関数)にこのビットを立てるようにしている。refill_inactive_clean関数では、この参照ビットが立っているページはAGE(参照頻度)を最大にし解放の対象外とする。また同時に参照ビットのクリアを行う。

refill_inactive_scan関数は、参照されていない(各種キャッシュ用の)ページであれば、解放候補リスト(INACTIVEリストに繋ぎ直す)

refill_inactive_scan()
        for(ACTIVEリスト上のページ) {
                if(ページに参照ビットPG_referencedが立っていたら) {
                        ページの参照ビットPG_referencedを落す
                        ページのAGEを元に戻す(age_page_up_nolock関数)
                } else {
                        ページのAGINGを行う(age_page_down_ageonly関数)
                        if(何処からも参照あれていないページ)
                                ページを解放対象ページとする(deactivate_page_nolock関数)
                }
                if(1ページ解放指定 && 解放成功) return;
        }

(NIS)HirokazuTakahashi
2000年12月09日 (土) 23時55分06秒 JST
1