このページを編集する際は、編集に関する方針に従ってください。

概要

  • mm/memory.cにて定義
  • 引数で渡されたアドレスに対応するページをスワップインする

引数

  • mm--ページフォルト例外を起こしたプロセスのメモリディスクリプタ
  • vma--メモリリージョン
  • address--例外を発生した仮想アドレス
  • page_table--アドレスaddressをマッピングしているページテーブルエントリのアドレス
  • pmd--ページミドルディレクトリ
  • orig_pte--アドレスaddressをマッピングしているページテーブルエントリの内容
  • write_access--読み取り/書き込みアクセス
    • 1なら書き込みが行われた

実装

/*

* We hold the mm semaphore and the page_table_lock on entry and
* should release the pagetable lock on exit..
*/

static int do_swap_page(struct mm_struct * mm,

	struct vm_area_struct * vma, unsigned long address,
	pte_t *page_table, pmd_t *pmd, pte_t orig_pte, int write_access)

{

	struct page *page;
  • (ページキャッシュまたはディスクからスワップインから)取得したいページ
	swp_entry_t entry = pte_to_swp_entry(orig_pte);
  • 例外を発生した仮想アドレスをマッピングしているページテーブルエントリのスワップエントリを作成
    • ページがスワップアウトすると、スワップエントリの情報がページテーブルエントリへ埋め込まれる
  • orig_pteからスワップエントリを作りentryへ設定する
	pte_t pte;
  • ページテーブルエントリ
	int ret = VM_FAULT_MINOR;
  • 返り値を初期化
    • VM_FAULT_MINORは副ページフォルト(カレントプロセスがブロックされることなくページフォルト処理できた)を意味する
	pte_unmap(page_table);
  • CONFIG_HIGHPTEが無効である場合、処理を行わない
	spin_unlock(&mm->page_table_lock);
  • スピンロックmm->page_table_lockの開放を行う。プリエンプション機能を有効にし、可能であれば自ら積極的にプリエンプション(実行権の移譲を行うこと)する
	page = lookup_swap_cache(entry);
  • 取得したいページをスワップキャッシュから探し、見つかった場合はpageへ設定する
    • スワップキャッシュに無い場合は、ディスクへスワップアウトされている(つまりスワップインする必要がある)
    • まだスワップアウトされていない場合はスワップキャッシュ内に見つけることが出来る
  • entryキーにして、スワップキャッシュからページを取り出し返す
	if (!page) {
  • 取得したいページのページ構造体がスワップキャッシュに無かった場合:
    • ページがスワップアウトされているので、ディスクからページをスワップインする
		swapin_readahead(entry, address, vma);
  • スワップエントリentryから始まる一連のページ群をスワップイン(スワップアウトされたページにスワップデバイスから情報を読み込む。スワップアウトされていない場合はページキャッシュからページを取り出す)する
		page = read_swap_cache_async(entry, vma, address);
  • スワップエントリentryに基づいてスワップイン(スワップアウトされたページにスワップデバイスから情報を読み込む)を行う
		if (!page) {
  • スワップイン出来なかった場合:
    • メモリ不足が発生したことが考えられる→最終的にはoutへジャンプ
			/*
			 * Back out if somebody else faulted in this pte while
			 * we released the page table lock.
			 */
			spin_lock(&mm->page_table_lock);
  • スピンロックmm->page_table_lockの取得を試みる
    • ロックの取得に失敗した場合は待ち状態に入る
    • 詳細はspin_lock()/linux2.6を参照
			page_table = pte_offset_map(pmd, address);
			if (likely(pte_same(*page_table, orig_pte)))
				ret = VM_FAULT_OOM;
  • page_tableとorig_pteの物理アドレスを比較する
    • 同じである場合は1を、異なる場合は0を返す
    • 詳細はpte_same()/linux2.6を参照
  • VM_FAULT_OOMはメモリ不足であったことを意味する
			else
				ret = VM_FAULT_MINOR;
  • VM_FAULT_MINORは副ページフォルト(カレントプロセスがブロックされることなくページフォルト処理できた)ことを意味する
			pte_unmap(page_table);
  • CONFIG_HIGHPTEが無効である場合、処理を行わない
			spin_unlock(&mm->page_table_lock);
  • スピンロックmm->page_table_lockの開放を行う。プリエンプション機能を有効にし、可能であれば自ら積極的にプリエンプション(実行権の移譲を行うこと)する
			goto out;
		}
		/* Had to read the page from swap area: Major fault */
		ret = VM_FAULT_MAJOR;
  • VM_FAULT_MAJORはページフォルトがカレントプロセスを待ち状態(ディスクからデータを読み込むための時間)にする必要があることを表す
		inc_page_state(pgmajfault);
		grab_swap_token();
	}
	mark_page_accessed(page);
	lock_page(page);
  • pageをロックし、既にロックされている場合はページの同期を取る
	/*
	 * Back out if somebody else faulted in this pte while we
	 * released the page table lock.
	 */
	spin_lock(&mm->page_table_lock);
  • スピンロックmm->page_table_lockの取得を試みる
    • ロックの取得に失敗した場合は待ち状態に入る
    • 詳細はspin_lock()/linux2.6を参照
	page_table = pte_offset_map(pmd, address);
	if (unlikely(!pte_same(*page_table, orig_pte))) {
  • ページのマッピングが変更されている場合:
  • page_tableとorig_pteの物理アドレスを比較する
    • 同じである場合は1を、異なる場合は0を返す
    • 詳細はpte_same()/linux2.6を参照
		pte_unmap(page_table);
  • CONFIG_HIGHPTEが無効である場合、処理を行わない
		spin_unlock(&mm->page_table_lock);
  • スピンロックmm->page_table_lockの開放を行う。プリエンプション機能を有効にし、可能であれば自ら積極的にプリエンプション(実行権の移譲を行うこと)する
		unlock_page(page);
  • pageのロックを解除し、このページが属するzoneのページロック解除待ちキューのエントリを起床を試みる
		page_cache_release(page);
		ret = VM_FAULT_MINOR;
  • VM_FAULT_MINORは副ページフォルト(カレントプロセスがブロックされることなくページフォルト処理できた)を意味する
		goto out;
	}
	/* The page isn't present yet, go ahead with the fault. */
		
	swap_free(entry);
  • スワップエントリentryに対応するページスロットの利用度数をデクリメントする
    • 利用度数が0になった場合、そのページスロットを解放したことを意味する
    • 詳細はswap_free()/linux2.6を参照
	if (vm_swap_full())
		remove_exclusive_swap_page(page);
  • スワップ領域上の50%以上を使用しているか調べる
  • pageがスワップキャッシュに属している場合、スワップキャッシュから解放する
	mm->rss++;
	pte = mk_pte(page, vma->vm_page_prot);
  • pageからページフレーム番号を算出し対応するページテーブルエントリ(PTE)を作り返す
	if (write_access && can_share_swap_page(page)) {
  • 書き込みアクセスで、かつpageが共有されていない場合:
  • スワップキャッシュのページpageが共有されているか調べる
		pte = maybe_mkwrite(pte_mkdirty(pte), vma);
  • ページテーブルエントリpteに更新(dirty)フラグを立てる
  • 引数で渡されたメモリリージョンが書き込み可能であれば、引数で渡されたページテーブルエントリを書き込み可能に設定する
		write_access = 0;
  • pageが共有されていないので、ページを書き込み可能にして、そのまま書き込めばよい
    • cow(コピーオンライト)する必要はない
	}
	unlock_page(page);
  • pageのロックを解除し、このページが属するzoneのページロック解除待ちキューのエントリを起床を試みる
	flush_icache_page(vma, page);
	set_pte(page_table, pte);
	page_add_anon_rmap(page, vma, address);
	if (write_access) {
		if (do_wp_page(mm, vma, address,
				page_table, pmd, pte) == VM_FAULT_OOM)
			ret = VM_FAULT_OOM;
  • コピーオンライトを実現する関数
    • ただし、該当するページを参照しているプロセスが1つだけである場合は、コピーオンライト処理を行わず、そのプロセスにページの書き込み権利を与え、将来の書き込みアクセス時にページフォルト例外が発生しないようにする
    • この関数はページが存在し、書き込みアクセス時、ページが書き込み禁止であるときに呼ばれる
    • 詳細はdo_wp_page()/linux2.6を参照
  • VM_FAULT_OOMはメモリ不足であったことを意味する
		goto out;
	}
	/* No need to invalidate - it was non-present before */
	update_mmu_cache(vma, address, pte);
	pte_unmap(page_table);
  • CONFIG_HIGHPTEが無効である場合、処理を行わない
	spin_unlock(&mm->page_table_lock);
  • スピンロックmm->page_table_lockの開放を行う。プリエンプション機能を有効にし、可能であれば自ら積極的にプリエンプション(実行権の移譲を行うこと)する

out:

	return ret;

}

呼出元


履歴

  • 作者:ひら
  • 日付:2005/9/14
  • 対象:2.6.10
    更新日更新者更新内容

コメント



トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2009-11-24 (火) 07:12:24 (2919d)