Linux Kernel(2.6)の実装に関するメモ書き

Swap - ページイン


1. ページイン処理の概要

ページアウトされたページはプロセスのアドレス空間からアンマップされているので、プロセスがページアウトされたアドレス空間にアクセスすると、ページフォルトが発生する。

ページフォルトのハンドラはユーザプロセスがページアウト済みのページにアクセスした場合に、do_swap_page()でページイン処理を開始する。

なお、ページフォルトの発生したアドレス空間のページがページアウトされているかどうかはPTEの内容で判断できる。ページアウトしてページをアンマップする際、ページアウト先の情報をPTEの空きフィールドに書き込むので、PTEにこの情報が格納されているかでページアウトされているかどうかを判断できる。


図1 ページイン処理の流れ


2. 処理の概要

2.1 do_swap_page()

ページフォルト発生後、ページがページアウトされていた場合は、do_swap_page()が呼び出される。

do_swap_page()はまずページフォルトが発生する要因となった仮想アドレス空間に対応するPTEからページアウト先の位置情報を取得する。この情報には、ページアウト先のスワップ領域種別とスワップ領域内のページオフセットが含まれる(「Swap - ページアウト」参照)。この情報により、ページインすべきデータがディスクのどこに格納されているかがわかる。

ページインすべきページがわかったら、そのページがすでにSwapCacheに格納されていないかを確認する。SwapCacheにすでにある場合はページインは不要なのでページのマップ処理に進む。SwapCacheにない場合は、swapin_readahead()でページのReadを開始する。

Readが完了すると、PTEを設定してプロセスのアドレス空間にページをマップしプロセスからアクセスできるようにする。

そして、swap_free()でスワップ空間内のこのページの使用中カウンタをデクリメントする。

do_swap_page()の処理概要
/* PTEがHighMemoryにある場合は、カーネル空間からUnmap
 * CONFIG_HIGHPTEが無効なら関係ない
 */
if (!pte_unmap_same(mm, pmd, page_table, orig_pte))
    goto out;

/* PTEからページアウト先の情報を取得 */
entry = pte_to_swp_entry(orig_pte);

again:

/* ページインするページがSwapCacheにあるかチェック */
page = lookup_swap_cache(entry);
if (!page) {
    /* Read I/O開始 - 先読みも行う */
    swapin_readahead(entry, address, vma);
    page = read_swap_cache_async(entry, vma, address);
    :
}
:

/* Lockを取得
 * 上でRead I/Oを開始した場合は、I/O完了までここでBlockする
 */
lock_page(page);

/* マップ処理
 * PTEを設定してプロセスからアクセスできるようにする
 */
pte = mk_pte(page, vma->vm_page_prot);
if (write_access && can_share_swap_page(page)) {
    /* ページフォルト要因がWriteアクセスで
     * このページを他に誰も使用していない場合は
     * SwapCache内のページをWriteしてしまってよいので
     * Write可にPTEを設定する。
     * この場合、Copy On Writeは不要なのでwrite_accsessを0にする。
     */
    pte = maybe_mkwrite(pte_mkdirty(pte), vma);
    write_access = 0;
}
set_pte_at(mm, address, page_table, pte);

/* ページをRmapに登録 */
page_add_anon_rmap(page, vma, address);

/* スワップ空間内のページの使用中カウンタをデクリメント */
swap_free(entry);
:
if (write_access) {
    /* ページフォルト要因がWriteアクセスで、
     * ページインしたページに直接Writeできない場合は、
     * 複製してそちらを使用する。- COW (Copy On Write)
     */
    if (do_wp_page(mm, vma, address,
                   page_table, pmd, ptl, pte) == VM_FAULT_OOM)
        ret = VM_FAULT_OOM;
    goto out;
}

2.2 swapin_readahead()

ページインのためRead I/Oを開始するルーチン。引数entryで指定したスワップ領域のページのReadと数ページ先の先読みも行う。Read I/Oは非同期で行われる。

swapin_readahed()の処理概要

/* Readするページ数を取得 */
num = valid_swaphandles(entry, &offset);

for (i = 0; i < num; offset++, i++) {
    /* 読み込みのための物理ページを割り当てて
     * SwapCacheに入れた後、Read I/Oを開始する。
     */
    new_page = read_swap_cache_async(swp_entry(swp_type(entry),
                                              offset), vma, addr);
    if (!new_page)
        break;
}

valid_swaphandles()はReadを行うページ数を返す。valid_swaphandles()は引数entryで指定されたスワップ領域内のページから連続で使用中(何らかのデータがページアウトされている)となっているページ数を返す(ただし上限は8 (*1))。

Readするページ数が決まると、read_swap_cache_async()を呼び出してRead I/Oを開始する。read_swap_cache_async()は読み込み先の物理ページを確保し、SwapCacheに登録する。そして、swap_readpage()でRead I/Oを開始する。

(*1) 上限は '1 << page_cluster'。メモリ16MB以上のシステムでpage_cluster = 3となる。


図2 swapin_readahead()の流れ

関連ページ


最終更新 2007/04/17 19:29:46 - kztomita
(2007/04/17 15:09:08 作成)
添付ファイル
pagein.png - kztomita
swapin_readahead.png - kztomita


リンク
最近更新したページ
検索