Jump to content 日本-日本語

製品 >  ソフトウェア >  HP- UX Developer Edge

HP-UXメモリ管理 ホワイト・ペーパー バージョン1.4

スワップ・スペース管理
HP-UX/Integrityサーバー お問い合せ
コンテンツに進む

スワップ・スペース管理


プロセスがforkされる際、その親プロセスの複製コピーにより、子プロセスの基礎が形成されます。

領域の種類によって異なる複製方法


カーネルルーチンprocdup()を使用して、システムが親プロセスのpregionリストをウォークし、子プロセス用に各pregionを複製します。この方法は、regionの種類によって異なります。

  • regionの種類がRT_SHAREDの場合、新しいpregionが作成され、親のregionに付加される。
  • regionの種類がRT_PRIVATEの場合、まずregionが複製され、次に、新しいpregionが作成されて新しいregionに付加される。

共有regionのpregionの複製


種類がRT_SHAREDのregionは、親と子で共有されるため、pregionとregionに対する変更はわずかです。新しいpregionのみ作成し、共有regionに付加する必要があります。

  • 新しいpregionが割り当てられ、親pregionから子pregionにフィールドがコピーされる。
  • vhandで使用されるpregion要素(p_agescan、p_ageremain、およびp_stealscan)がゼロに初期化され、子pregionがstealhandの直前のアクティブなpregionチェーンに追加される。これで、子pregionがスチールされることを防止できる。
  • regionにアクセスするメモリ内のpregionの数と、regionにアクセスするメモリ内またはページングされたpregionの数を反映するように、region要素r_incoreとr_refcntが増分される。
図28 共有regionのpregionの複製
図28 共有regionのpregionの複製

専用regionのpregionの複製


RT_PRIVATE regionのコピー手順は、RT_SHARED regionよりも複雑です。

  • 新しいregionが割り当てられる。
  • 子regionのポインタが設定される。

    • 順方向格納ポインタr_fstoreが親と同じ値を指し示すように設定され、vnodeの参照カウント(v_count)が増分される。
    • 逆方向格納ポインタr_bstoreがカーネル・グローバルswapdev_vpに設定され、そのv_countも増分される。
  • 子regionが、アクティブなregionのリンク・リストの最後に付加される。
  • スワップが予約される。使用可能なスワップ・スペースが不十分な場合、fork()が失敗し、エラーENOMEMが返される。
  • regionのB-tree構造が初期化され、完全に満たされたB-tree用の十分なスワップ・スペースが予約される。
  • 親のvfdプロトタイプ値とdbdプロトタイプ値が子のB-treeルートにコピーされる。
  • regionのすべてのページが「書き込み時コピー」されるように、親regionと子regionの両方のvfdプロトタイプが設定される。
  • B-treeに追加された新しいvfddbdペア用にvfdで「書き込み時コピー」フラグ(pg_cw)を設定しなければならないことを示すB-tree要素b_vprotoが設定される。
  • vfddbdのチャンクが子のB-tree用に作成され(親のB-tree内のvfddbdの各チャンクと等しい)、プロトタイプ値で満たされる。pg_cwビットは、子B-treeのチャンク内のすべてのデフォルトvfdについて「書き込み時コピー」にすでに設定されている。
図29 RT_PRIVATE regionの複製
図29 RT_PRIVATE regionの複製

vfdが有効な場合の「書き込み時コピー」の設定


子region内のvfddbdのチャンクを使用する前に、すべてのエントリの有効性をチェックする必要があります。

  • vfdが有効でない(そのpg_vが設定されていない)場合、親のvfdのpg_cwを設定し、子にコピーする必要がある。pg_lockが親内で設定されている場合、ロックが継承されないため、子内の設定を解除する必要がある。
vfdが有効であれば、低レベル構造がさらに変更されます。

  • 有効ページ数を反映するように、子region内のr_nvalid要素が増分される。
  • vfdには、pfdat[]配列への索引であるpfn(ページ・フレーム番号)が含まれます。pfdatエントリpf_useカウント(このページを使用するregionの数)を増分する必要があります。
  • 親vfdの「書き込み時コピー」ビットが設定されていない場合、ページへの変換が「書き込み時コピー」として動作するようにpdeを設定する必要がある。

ページとスワップ・イメージの調和


ページがスワップ・デバイスに書き込まれ、その後に変更されている場合は、スワップ・デバイス・データがメモリ内のデータと相違します。 dbdのタイプをDBD_NONEに設定して、ディスク・ページとメモリ内のページの結合を解除する必要があります。次回、スワップ・デバイスにページを次回書き込む際に、ページが新しい位置に割り当てられます。

これで、親のB-treeの観点からすべてが「書き込み時コピー」にセットアップされます。

子regionの「書き込み時コピー」ステータスの設定


  • 子のr_swallocが、予約されたregionとB-treeのページ数に設定される。
  • r_prevとr_nextが、子regionを親regionにリンクするように設定される。
  • カーネルが、親pregionから新しいスペースをコピーするのではなく、pregion用の新しいスペースを選択する。これにより、2つの範囲の仮想アドレス(スペースは異なり、オフセットは同じ)から1つの範囲の物理アドレスへの変換が確立される。

    • 親プロセスがその仮想アドレスにアクセスすると、そのアドレスがTLBから消去されているためTLBミス・フォールトが発生する。
    • 子プロセスがその仮想アドレスのいずれかにアクセスすると、そのアドレスがTLBに以前存在しなかったためTLBミス・フォールトが発生する。

プロセスのアドレス空間の複製


  • procdup()が、forkタイプ、親プロセス(pp)、子プロセス(cp)、親スレッド(pt)、および子スレッド(ct)に基づいてプロセスの複製コピーを作成する。

    procdup()が、子のuarea用のメモリを割り当てる(実際には、procdup()は、uareaを作成するcreateU()を呼び出すルーチン)。

    procdup()が、実行されているプロセスの種類(forkまたはvfork)にもとづいて親の仮想アドレス空間を複製するdupvas()を呼び出す。

  • プロセスがforkで作成された場合、dupvas()が親プロセスの仮想アドレス空間を複製する。プロセスがvforkされた場合、親の仮想アドレスが使用される。

    dupvas()が、(複製する必要があるデータ・オブジェクトがどれであっても)各専用データ・オブジェクトを検索し(テキスト、メモリ・マッピング、データ・オブジェクト、グラフィックスで求められる特別な留意事項がある)、特殊オブジェクトの複製が終了すると、専用regionまたは共有regionのどちらを処理するかに応じてprivate_copy()またはshared_copy()を呼び出す。

    • 共有regionの場合、共有されていることを示すためにshared_copyでregion上の参照カウントが増分される。
    • 専用regionの場合、private_copyがregionをロックし、dupreg()の呼び出しによりregionを複製できるようにする。
  • dupreg()が子用の新しいregionを割り当て、親のvfdとregion構造全体を複製する。次に、do_dupc()を呼び出してregionの下のエントリを複製する。
  • do_dupc()が親/子関係をセットアップし、その関係を複製することで、子を「書き込み時コピー」にセットアップする。また、do_dupc()は、親のregionが有効であることを確認し、子を「書き込み時コピー」に設定する。さらに、変換をrx(読み取り実行)のみに設定し、region内のvfddbdの組み合わせすべての情報を複製する。
  • 次に、do_dupcがhdl_cw()を呼び出して、子のアクセス権を更新し、子を「書き込み時コピー」にする。
これが完了すると、子プロセスが親プロセスの複製バージョンとして存在します。子プロセスが子のアドレス空間に付加されて、親に依存しなくなります。

子プロセスのuareaの複製


プロセスの各スレッドに、固有のuareaがあります。プロセスがfork()する場合、新しいプロセスにはスレッドが1つのみ存在し、そのスレッドにuareaが必要です。procdup()がcreateU()を呼び出して、このuareaを作成します(uarea pregionはdupvas()でコピーされないため、親のスレッド(および、関連するuarea)の数に関係なく、子のuareaは1つのみになります)。

createU()ルーチンが、子プロセスのuareaとアドレス空間を生成します。forkされたプロセス用に最後にuareaがセットアップされ、pregionの複製コードの中間で子プロセスが再開されることが防止されます。このプロセスがvforkされると、exec()中にuareaが作成されます。それまで、子プロセスは親プロセスのスレッドのuareaを使用します。

  • ユーザー・プロセスがFORK_PROCESSで作成される際、子のuareaに変更される親のuareaの作業コピー用に一時的スペースが割り当てられる。この一時的スペースは、uareaが新しいregionにコピーされた後、解放される。データがコピーされた直後、fork()が親のuareaのu_pcb内のsavestateを更新する。(vfork()では、exec()中にuareaが作成されてsavestateがすぐに変更されるため、この更新は行われない)。
  • 新しいuarea用にregionが割り当てられ、そのデータ構造が初期化される。また、そのr_bstore値がスワップ・デバイスに設定され、新しいregionがアクティブなregionのリストに追加される。uareaにはすでにデータが入っているため、uareaにはr_fstoreの値が設定されない。
  • uareaのpregion用にスペースが割り当てられ、初期化される。各uareaには、一意のスペースIDがある。新しいpregionは、PF_NOPAGEフラグでマークされる。uarea pregionはアクティブなpregionのリストに追加されないため、vhandの影響は受けない。プロセス全体がスワップアウトされる場合のみ、uareaのページがスワップ・デバイスに書き込まれる。
  • 作成されたpregionは、vasに接続されているpregionのリンク・リストに付加される。そのポインタがr_pregsに保存され、p_prpnextがヌルに設定され、r_incoreとr_refcntが1に設定される。
  • uareaとB-treeページ用にスワップ・スペースが予約され、デフォルトのdbdがDBD_DFILLに設定されると、uareaページ(UPAGES)が割り当てられる。各ページには、1ページの物理メモリが必要(すぐに使用可能なメモリがない場合は休眠状態となる)。pfnがvfdに保存され、pg_vが有効として設定される。また、r_nvalidが増分され、pdeが物理から仮想への変換用に作成される。pfdatエントリのP_UAREAフラグとHDLPF_TRANSフラグが設定され、dbdがDBD_NONEに設定される。
  • 子のスレッド構造内のポインタkt_upregが、このスレッドのuarea pregionを指し示すように設定される。
これで子の実行が成功すると考えられます。setjmp()呼び出しでコピーされpcb_sswapで指し示されたuareaに、現在の状態が保存されます。このため、子がresume()ルーチンを最初に呼び出すと、このルーチンがpcb_sswapがゼロ以外であることを認識し、longjmp()を行ってここに戻ります。次に、子が戻り値FORKRTN_CHILDでprocdup()から返されます。

親のオープン・ファイル・テーブルが子にコピーされ、コピー済みのuareaが実際のpregionにコピーされます。このコピーによってTLBミス・フォールトが発生し、その結果pregionのpdeがTLBに書き込まれます。そのため、uareaの仮想アドレスが、セットアップされたばかりの物理ページに関連付けられます。戻り値FORKRTN_PARENTでprocdup()から返されることで、このプロセスが完了します。

親の「書き込み時コピー」ページからの読み取り


親が読み取りのためにそのRT_PRIVATEページの1つにアクセスすると、プロセッサーが、カーネルにより割り込みとして処理されるTLBミス・フォールトを生成します。TLBミス・フォールト・ハンドラがhpdeを検索し、その情報(新しいアクセス権を含む)をプロセッサーのTLBに挿入します。PDE_AR_CWによってユーザー・モードの読み取りアクセスが許されるため、割り込みから戻る際、プロセッサーが読み取りを再試行し、成功します。

図30 「書き込み時コピー」ページに対する初回の読み取り
図30 「書き込み時コピー」ページに対する初回の読み取り

子の「書き込み時コピー」ページからの読み取り


子が読み取りのためにそのページの1つにアクセスするとき、まだ何もセットアップされていないため、TLBミス・ハンドラは仮想アドレスのhpdeを検索しません。この仮想アドレスは、pregionでセットアップされています。「アクセス時コピー(copy-on-access)」(現在のデフォルト)を行わない場合、ページが必要になると、エイリアス指定された変換を行う必要があります。

  • 最初にsave_stateが作成される。
  • vasポインタが取得され、このアドレスのページを含むpregionを見つけるためにスキップ・リストが検索される。
  • このページが複数の仮想アドレスに変換される場合、適切なエイリアスが得られる。
  • 子領域が読み取りのためのページへのアクセスに失敗し、TLBミスが発生するが、ミス・ハンドラが変換を見つけてTLBにロードする。
  • ルーチンが割り込みから戻り、ページの読み取りに成功する。

ページのフォールトイン


領域の初期化時、ディスク・ブロック記述子(dbd)のdbd_dataフィールドが必ずDBD_DINVAL (0x1fffffff)に設定されます。プロトタイプdbd_type値は、次のように設定されます。

  • テキストと初期化されたデータの場合、DBD_FSTORE。
  • スタックと初期化されていないデータの場合、DBD_DZERO。
ページが最初に読み取られる際、物理ページ(および、スパースPDIR内のその変換)がまだ存在しないため、TLBミス・フォールトが発生します。このページを取り込み、フォールトが発生した命令を再開するのがフォールト・ハンドラです。フォールト・ハンドラは、ページが有効かどうかを判断するために、フォールトが発生しているプロセスのどのpregionにフォールトが発生しているアドレスが含まれるかを判別します。最終的にフォールト処理コードによって、主要な仮想フォールト処理ルーチンvirtual_fault()が呼び出されます。このルーチンに渡される引き数は、フォールトを引き起こした仮想アドレス、pregion、および読み取りまたは書き込みアクセスを示すフラグです。

カーネルがページのvfdとdbdを見つけるためにB-treeを検索します。vfdフラグに有効ビットが設定されている場合、別のプロセスがすでにアドレスをメモリに読み込んでいます。領域内でr_zombフラグが設定されている場合、カーネルがPid %d killed due to text modificationまたはページI/Oエラーメッセージを出力し、SIGKILLを返します。SIGKILLは、ハンドラによりプロセスに送られます。

スタックまたは未初期化データのページのフォールトイン


dbd_type値がDBD_DZEROに設定されている場合(スタックと初期化されていないデータの場合と同様に)、プロセスが「書き込み時コピー」ビットをゼロに設定します。次にカーネルが、ページがシステム・プロセスまたは高優先順位のスレッドのどちらに関係があるかチェックします。どちらにも関係がなく、メモリが不足気味の場合、プロセスに関連付けられた優先順位まで空きメモリが下がるまでプロセスは休眠状態になります(最悪の場合、メモリがdesfreeを上回るまで、スレッドが待機することがあります)。

プロセスが再開すると、精度を保つためにvfdポインタとdbdポインタが調べられます。空きpfdatエントリが物理メモリ・アロケータから取得され、そのpfn (pf_pfn)がvfdに配置されます。また、vfdの有効ビットが設定され、領域のr_nvalidカウンタ(有効なページ数)が増分されます。次に、ページ数がゼロにされ、仮想から物理への変換がスパースPDIRに追加されます。最後に、カーネルがdbd_typeをDBD_NONEに、dbd_dataを0xfffff0cに変更します。

テキストまたは初期化済みデータのページのフォールトイン


プロセスのDBD_FSTOREページ上に仮想フォールトがある場合、カーネルはvnodeへのr_fstoreポインタを使用して、呼び出すファイル・システム特有のpagein()ルーチン(たとえば、ufs_pagein()、nfs_pagein()、cdfs_pagein()、vx_pagein())を決めます。pagein()ルーチンは、メモリ・ページの空きリストからの正しいページの回復、またはディスクからの正しいページでの読み取りに使用されます。

pagein()ルーチンは、フォールトが発生しているページに関する情報をvm_pagein_init()ルーチンから取得します。vm_pagein_init()ルーチンはvfd/dbdの取得、領域索引のセットアップ、および有効なページがまだ存在しないことの確認を行います。

1つのページを予約する必要があります。ゼロが満たされたページ(スパース・ファイル)またはページ・キャッシュのいずれかでページ・フォールトをローカルに満たすことができるかどうかを判定するために、vm_no_io_required()が呼び出されます。

vm_no_io_required()は、lgpg_cache_lookup()を呼び出して、ページ・キャッシュ内にフォールトが発生したページがあるかどうかチェックします。

lgpg_cache_lookup()は、pageincache()を使用して基本ページを検索し、次にlgpg_lookup()を使用して基本ページが適切な大きさのページの一部であるかどうか判断します。

pageincache()は、vnodeポインタとデータをハッシュして、phash[]内のpfdatポインタを選択します。また、pfdatエントリのpf_hchainチェーンをウォークして、一致するvnodeポインタ(pf_devvp)とデータ値(pf_data)を検索します。一致が見つかると、空きリストから削除されます。

ページ・キャッシュ内でページが見つかると、その領域の有効なページ・カウント(r_nvalid)が増分され、vfdがpfn(pf_pfn)で更新されます。また、そのページの仮想から物理への変換がスパースPDIRに追加されます(変換が削除されている場合)。

図31 DBD_FSTOREページのフォールトインに対するページ・キャッシュのチェック
図31 DBD_FSTOREページのフォールトインに対するページ・キャッシュのチェック

ディスクからのテキストまたは初期化済みデータのページの取り出し


必要なページがページ・キャッシュ内で見つからない場合、pagein()ルーチンがdbdを参照して、フェッチ対象のページを確認します(この情報はvm_no_io_required()によってdbdに保存されています)。一般にpagein()ルーチンは、フォールトが発生している単一のページよりも多くのページの読み取りを試みます。つまり、4KBよりも大きなページの使用(利用可能なメモリやファイル属性などの状況により、そうすることが可能な場合)と、順次アクセスをしているファイルからの余分なページの単純な先読みの両方を行います(そのファイル上で次にページ・フォールトが発生したときに、余分なページを使用できるようにします)。

メモリのページ(1つまたは複数)が物理メモリ・アロケータから割り当てられ、仮想から物理への変換がスパースPDIRに追加されます。また、ディスクからページへのI/Oがスケジュールされ、プロセスが非先読みI/Oの完了を待機して休眠状態になります(プロセスは先読みI/Oの完了を待機しません)。vfdが有効とマークされます。dbdのdbd_typeはDBD_FSTOREに設定され、dbd_dataはディスク上のブロック・アドレスに設定されたままになります。

ページ・データにゼロが満たされのか、それとも空きリストまたはディスクのいずれから取り出されたのかにかかわらず、ページ・ディレクトリ・エントリ(pde)が触れられます。命令が再試行されて、TLBミス・フォールトが発生します。ミス・ハンドラが変更されたpdeデータをTLBに書き込みます。命令がまた再試行されて成功します。
最初のページへ 前のページへ 次のページへ

お問い合わせ

ご購入前のお問い合わせ


ご購入後のお問い合わせ

HPEサポートセンター
製品の標準保証でご利用いただける無償のサービスです。

ショールーム

ショールーム 導入をご検討のお客様へ
業務アプリケーションの継続・標準化・開発性とシステム担当者様、システム開発者様が抱える悩み・疑問に対する解決策実体験して頂けます。
印刷用画面へ
プライバシー ご利用条件・免責事項