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

概要

  • mm/memory.cにて定義
  • startからstart + lenの間のすべてのページに物理ページが割り当てられているか調べる
    • 割り当てられていない場合は、物理ページを割り当てる

引数

  • tsk--タスクディスクリプタ
  • mm--メモリディスクリプタ
  • start--開始アドレス:物理ページが割り当てられているか確認する先頭アドレス
  • len--範囲:start + lenの領域に物理アドレスが割り当てられているか確認する
  • write--書き込み可能か
    • 書き込み可能である場合は1を、そうでない場合は0が設定される
  • force--
  • pages--NULLでない場合は、取得したページ群を格納
  • vmas--NULLでない場合は、取得したページのメモリリージョン群

実装

int get_user_pages(struct task_struct *tsk, struct mm_struct *mm,

		unsigned long start, int len, int write, int force,
		struct page **pages, struct vm_area_struct **vmas)

{

	int i;
	unsigned int flags;
	/* 
	 * Require read or write permissions.
	 * If 'force' is set, we only require the "MAY" flags.
	 */
	flags = write ? (VM_WRITE | VM_MAYWRITE) : (VM_READ | VM_MAYREAD);
	flags &= force ? (VM_MAYREAD | VM_MAYWRITE) : (VM_READ | VM_WRITE);
	i = 0;
	do {
		struct vm_area_struct *	vma;
		vma = find_extend_vma(mm, start);
		if (!vma && in_gate_area(tsk, start)) {
  • startが固定ユーザ領域内であれば1を返す
			unsigned long pg = start & PAGE_MASK;
			struct vm_area_struct *gate_vma = get_gate_vma(tsk);
			pgd_t *pgd;
			pmd_t *pmd;
			pte_t *pte;
			if (write) /* user gate pages are read-only */
				return i ? : -EFAULT;
			if (pg > TASK_SIZE)
				pgd = pgd_offset_k(pg);
			else
				pgd = pgd_offset_gate(mm, pg);
  • mmにおける仮想アドレスpgに対応するページグローバルディレクトリテーブルのエントリのインデックスを返す
			BUG_ON(pgd_none(*pgd));
			pmd = pmd_offset(pgd, pg);
			BUG_ON(pmd_none(*pmd));
  • 引数で与えられたページミドルディレクトリが存在しない場合は1を返す
			pte = pte_offset_map(pmd, pg);
			BUG_ON(pte_none(*pte));
  • ページテーブルpteの有効/無効を調べる
    • 無効である場合は1を、有効である場合は0を返す
    • 詳細はpte_none()/linux2.6を参照
			if (pages) {
				pages[i] = pte_page(*pte);
  • ページテーブルエントリpteから対応するページ構造体(page/linux2.6)を見つける
				get_page(pages[i]);
			}
			pte_unmap(pte);
  • CONFIG_HIGHPTEが有効で無い場合は、処理を行わない
			if (vmas)
				vmas[i] = gate_vma;
			i++;
			start += PAGE_SIZE;
			len--;
			continue;
		}
		if (!vma || (vma->vm_flags & VM_IO)
				|| !(flags & vma->vm_flags))
			return i ? : -EFAULT;
		if (is_vm_hugetlb_page(vma)) {
  • メモリリージョンvmaにおいてhuge TLB(ラージページ)が有効である場合は1を返す
			i = follow_hugetlb_page(mm, vma, pages, vmas,
						&start, &len, i);
  • start から (start + len) のメモリ領域がhuge TLB(ラージページ)であることを確認する
			continue;
  • 繰り返しを終了する
		}
		spin_lock(&mm->page_table_lock);
  • スピンロックmm->page_table_lockの取得を試みる
    • ロックの取得に失敗した場合は待ち状態に入る
    • 詳細はspin_lock()/linux2.6を参照
		do {
			struct page *map;
			int lookup_write = write;
			while (!(map = follow_page(mm, start, lookup_write))) {
  • 仮想アドレスstartに対応するページを見つけて返す。
				/*
				 * Shortcut for anonymous pages. We don't want
				 * to force the creation of pages tables for
				 * insanly big anonymously mapped areas that
				 * nobody touched so far. This is important
				 * for doing a core dump for these mappings.
				 */
				if (!lookup_write &&
				    untouched_anonymous_page(mm,vma,start)) {
					map = ZERO_PAGE(start);
  • ゼロページを返す。ゼロページはページフレーム割り当てを遅延させるために用いられる
    • ゼロページはグローバルな共有ページである
    • ゼロページには書き込み不可の印が付けてあるため、プロセスが書き込もうとすると、コピーオンライト機構が働く
      • この場合に限り、プロセスは書き込むことができるページを獲得することができる
    • 詳細はZERO_PAGE()/linux2.6を参照
					break;
				}
				spin_unlock(&mm->page_table_lock);
  • スピンロックmm->page_table_lockの開放を行う。プリエンプション機能を有効にし、可能であれば自ら積極的にプリエンプション(実行権の移譲を行うこと)する。
				switch (handle_mm_fault(mm,vma,start,write)) {
  • ページフレームを確保し、ページテーブルエントリ(PTE)に登録する
				case VM_FAULT_MINOR:
  • 副ページフォルト(minor fault)が発生したことを意味する:
    • 副ページフォルトはカレントプロセスがブロックされることなくページフォルト処理できたことを意味する
					tsk->min_flt++;
  • 副ページフォルト(minor fault)の発生回数をインクリメント
					break;
				case VM_FAULT_MAJOR:
  • 主ページフォルト(major fault)が発生したことを意味する:
    • 主ページフォルトはページフォルトがカレントプロセスを待ち状態(ディスクからデータを読み込むための時間)にする必要があることを表す
      					tsk->maj_flt++;
  • 主ページフォルト(major fault)の発生回数をインクリメント
					break;
				case VM_FAULT_SIGBUS:
					return i ? i : -EFAULT;
				case VM_FAULT_OOM:
  • メモリ不足であったことを意味する:
					return i ? i : -ENOMEM;
				default:
					BUG();
				}
				/*
				 * Now that we have performed a write fault
				 * and surely no longer have a shared page we
				 * shouldn't write, we shouldn't ignore an
				 * unwritable page in the page table if
				 * we are forcing write access.
				 */
				lookup_write = write && !force;
				spin_lock(&mm->page_table_lock);
  • スピンロックmm->page_table_lockの取得を試みる
    • ロックの取得に失敗した場合は待ち状態に入る
    • 詳細はspin_lock()/linux2.6を参照
			}
			if (pages) {
				pages[i] = get_page_map(map);
  • mapが物理メモリにマッピングされたページであるか調べる
    • マッピングされていない場合はNULLを、そうでない場合は引数で渡されたpageをそのまま返す
    • 詳細はget_page_map()/linux2.6を参照
				if (!pages[i]) {
					spin_unlock(&mm->page_table_lock);
  • スピンロックmm->page_table_lockの開放を行う。プリエンプション機能を有効にし、可能であれば自ら積極的にプリエンプション(実行権の移譲を行うこと)する。
					while (i--)
						page_cache_release(pages[i]);
					i = -EFAULT;
					goto out;
				}
				flush_dcache_page(pages[i]);
				if (!PageReserved(pages[i]))
					page_cache_get(pages[i]);
  • ページpages[i]が予約されているか調べる
    • 予約されている場合1を返し、予約されていない場合は0を返す
    • 詳細はPageReserved()/linux2.6を参照
  • ページpages[i]の参照数をカウントアップ(+1)する
			}
			if (vmas)
				vmas[i] = vma;
			i++;
			start += PAGE_SIZE;
			len--;
		} while(len && start < vma->vm_end);
  • startからメモリリージョンvmaの最後になるか、lenが0になるまで繰り返す
		spin_unlock(&mm->page_table_lock);
  • スピンロックmm->page_table_lockの開放を行う。プリエンプション機能を有効にし、可能であれば自ら積極的にプリエンプション(実行権の移譲を行うこと)する。
	} while(len);

out:

	return i;

}

EXPORT_SYMBOL(get_user_pages);

呼出元


  • 1 -- 2012-02-18 (土) 00:55:27

履歴

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

コメント

  • 1 -- 1? 2012-02-18 (土) 00:55:35
  • 1 -- 1? 2012-02-18 (土) 00:55:43
  • 1 -- 1? 2012-02-18 (土) 00:55:47
  • 1 -- -1'? 2012-02-18 (土) 00:55:49
    • 1' -- 1? 2012-02-18 (土) 00:55:50
  • 1 -- 1? 2012-02-18 (土) 00:55:52

  • 1 -- 1? 2012-02-18 (土) 00:55:45

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2012-02-18 (土) 00:55:53 (2103d)