[ 前のページ ] [ 次のページ ] [ 目次 ] [ 索引 ] [ DOC Home ]

5 ページ・サイズの拡大に対するアプリケーションの対応

この章では,アプリケーションで VAXのページ・サイズに依存している部分を識別する方法を説明し, これらの問題に対する対処方法を示します。


5.1 概要

ページ・サイズは,オペレーティング・システムが操作するメモリの基本単位であり, 一般にアプリケーションのレベルでは意識する必要はありません。特に, 高級プログラミング言語や中級プログラミング言語で作成されたアプリケーションの場合には, ページ・サイズを直接操作することはほとんどありません。しかし, アプリケーションでシステム・サービスやランタイム・ライブラリ・ルーチンを呼び出し, 次のようなメモリ管理機能を実行する場合には, ページ・サイズに依存する部分がアプリケーションに含まれている可能性があります。

これらを実行するシステム・サービスやランタイム・ライブラリ・ルーチンは, メモリをページ単位で操作します。 これらのルーチンに対する引数として値を指定する場合は,1ページが 512バイトであるものと仮定しています。これは VAXアーキテクチャで定義されているページ・サイズです。Alphaアーキテクチャでは, 8Kバイト,16Kバイト,32Kバイト,または 64Kバイトのページ・サイズをサポートします。したがって, ルーチンへの引数として指定する値を調べ, それらの値がアプリケーションの必要条件を満足するかどうかを確認しなければなりません。 この後の節では,これらのルーチンを調べる方法について詳しく説明します。

このようなページ・サイズの違いは, 上位レベル・ルーチンを使用するメモリ割り当てには影響を与えません。たとえば, Cの malloc freeルーチンなど, 言語固有のメモリ割り当てルーチンや, 仮想メモリ領域を操作するランタイム・ライブラリ・ルーチンなどは, ページ・サイズの違いの影響を受けません。


5.1.1 互換性のある機能

システム・サービスやランタイム・ライブラリ・ルーチンは, Alphaシステムにおいてもできる限り VAXシステムと同じインターフェイスや戻り値を維持しています。たとえば Alphaシステムでは, 引数としてページ・カウント値を受け付けるルーチンのこれらの引数を ページレットと呼ぶ512バイトの量として解釈することにより, CPU固有のページ・サイズと区別します。 各ルーチンはページレットの値をCPU固有のページに変換します。 ページ・カウント値を戻すルーチンは, CPU固有のページからページレットに変換することにより, アプリケーションで期待される戻り値が512バイト単位で表現されるようにします。


注意
Alphaシステムでは,$CRMPSCシステム・サービスを使用して(さらに SEC$M_PFNMAPフラグ・ビットをセットして) ページ・フレーム・セクションを作成する場合には, ページ・カウント引数(pagcnt)に指定された値は CPU固有のページ・サイズとして解釈され, ページレットの値としては解釈されません


5.1.2  特定のページ・サイズに依存する可能性のあるメモリ管理ルーチンのまとめ

互換性があるにもかかわらず,一部のルーチンのAlphaシステムでの動作は VAXシステムでの動作と異なっており, ソース・コードを変更しなければならない可能性があります。たとえば Alphaシステムでは,セクション・ファイルをマッピングするシステム・サービス ($CRMPSCと$MGBLSC)は, CPU固有のページ境界にアラインされたアドレス値を引数として指定しなければなりません。 VAXシステムでは,これらのルーチンは引数のアドレス値を VAXページ境界になるように調整します。Alphaシステムでは, これらのアドレスはCPU固有のページ境界には調整されません。

表 5-1は, ページ・サイズに依存する部分を含んでいる可能性のあるメモリ管理ルーチンと, それらのルーチンがサポートする引数を示しています。この表には,各引数の機能と, これらの引数がルーチンのOpenVMS Alphaバージョンでどのように解釈されるかが示されています。 この表には,ルーチンが受け付けるすべての引数が示されているわけではありません。 ルーチンとその引数リストについての詳しい説明は, 『OpenVMS System Services Reference Manual』を参照してください。

表 5-1 メモリ管理ルーチンでページ・サイズに依存する可能性のある部分 :(クリックで表示)

表 5-2に示したランタイム・ライブラリ・ルーチン は,メモリ・ページを割り当てるか,または解放します。互換性を維持する ために,これらのルーチンでは,ユーザが指定したページ・カウント情報を ページレットの値として解釈します。

表 5-2  ランタイム・ライブラリ・ルーチンでページ・サイズに依存する可能性のある部分

ルーチン 引き数 Alphaシステムでの解釈
LIB$GET_VM_PAGE number-of-pages
割り当てる連続ページのページ数を指定する。
ページレット単位の値として解釈され,CPU固有のページに切り上げられるか, または切り捨てられる。
LIB$FREE_VM_PAGE number-of-pages
割り当てを解除する連続ページのページ数を指定する。
ページレット単位の値として解釈され, CPU固有のページになるように切り上げられるか,または切り捨てられる。


5.2 メモリ割り当てルーチンの確認

アプリケーションが実行するメモリ割り当てを変更しなければならないかどうかを判断するには, メモリがどこで割り当てられるかを確認しなければなりません。 メモリ割り当てを実行するシステム・サービス・ルーチン ($EXPREGと$CRETVA)を使用すれば,次の 2種類の方法でメモリを割り当てることができます。

Alphaアーキテクチャでは, VAXアーキテクチャと同じ仮想アドレス空間レイアウトを定義しており, VAXシステムの場合と同じ方向にP0領域とP1領域を拡大できます。 図 5-1はこのレイアウトを示しています。

図 5-1 仮想アドレスのレイアウト :(クリックで表示)


5.2.1  拡張された仮想アドレス空間でのメモリの割り当て

アプリケーションで$EXPREGシステム・サービスを使用して 仮想アドレス空間を拡張することによりメモリを割り当てる場合には, ソース・コードを変更する必要はありません。これは, VAXシステムで引数として指定した値がAlphaシステムでも正しく動作するからです。 この理由は次のとおりです。

対処方法

アプリケーションを変更する必要はありません。しかし, $EXPREGシステム・サービスが戻すメモリ・サイズは, Alphaアーキテクチャを実現した各システムで異なる可能性があるため, システムが割り当てた正確なメモリ境界を確認しておくことが適切でしょう。 正確なメモリ境界を確認するには, $EXPREGシステム・サービスに対して省略可能な引数である retadr 引数を指定します(アプリケーションでこの引数がまだ指定されていない場合)。 retadr 引数には, システム・サービスが割り当てたメモリの先頭アドレスと末尾アドレスが格納されます。

たとえば,例 5-1のプログラムは, $EXPREGシステム・サービスを呼び出すことにより10ページの追加メモリを要求します。 このプログラムをVAXシステムで実行した場合には,$EXPREGシステム・サービスは 5120バイトの追加メモリを割り当てます。このプログラムを Alphaシステムで実行した場合には,$EXPREGシステム・サービスは少なくとも 8192バイトを割り当てます。また, Alphaアーキテクチャの特定の動作のページ・サイズによっては, それ以上のサイズのメモリを割り当てることもあります。

例 5-1  仮想アドレス空間の拡張によるメモリの割り当て

     #include  <ssdef.h>
     #include  <stdio.h>
     #include  <stsdef.h>
     #include  <descrip.h>
     #include  <dvidef.h>

     #define  PAGE_COUNT 10   【1】
     #define  P0_SPACE   0
     #define  P1_SPACE   1

     main( argc, argv )
     int argc;
     char *argv[];
     {
          int    status = 0;
          long   bytes_allocated, addr_returned[2];

  【2】   status = SYS$EXPREG( PAGE_COUNT, &addr_returned, 0, P0_SPACE);

          bytes_allocated = addr_returned[1] - addr_returned[0];

          if( status == SS$_NORMAL)
             printf("bytes allocated = %d\n", bytes_allocated );
          else
             return (status);

     }
この後の各項目は,例 5-1に示した番号に対応します。

【1】 この例では,要求するページ数を意味するシンボルとして PAGE_COUNTを定義しています。
【2】 この例では,仮想アドレス空間のP0領域の末尾に 10ページを追加することを要求しています。


5.2.2 既存の仮想アドレス空間でのメモリの割り当て

アプリケーションで$CRETVAシステム・サービスを使用することにより, 仮想アドレス空間内のメモリを再割り当てする場合には,$CRETVAに対する引数のうち, 次の引数の値を変更する必要があるかもしれません。

対処方法

アプリケーションを変更しなければならないかどうかを判断するには, 次の操作を実行してください。

例 5-2は,バッファに割り当てたメモリを $CRETVAシステム・サービスによって再割り当てする方法を示しています。

例 5-2 既存のアドレス空間でのメモリの割り当て

     #include  <ssdef.h>
     #include  <stdio.h>
     #include  <stsdef.h>
     #include  <descrip.h>
     #include  <dvidef.h>

     char _align(page) buffer[1024];

     main( argc, argv )
     int argc;
     char *argv[];
     {

          int      status = 0;
          long     inadr[2];
          long     retadr[2];

          inadr[0] = &buffer[0];
          inadr[1] = &buffer[1023];

          printf("inadr[0]=%u,inadr[1]=%u\n",inadr[0],inadr[1]);

          status = SYS$CRETVA(inadr, &retadr, 0);

          if( status & STS$M_SUCCESS )
          {
             printf("success\n");
             printf("retadr[0]=%u,retadr[1]=%u\n",retadr[0],retadr[1]);
          }
          else
          {
             printf("failure\n");
             exit(status);
          }
     }


5.2.3 仮想メモリの削除

$EXPREGシステム・サービスと $CRETVAシステム・サービスによって割り当てたメモリを解除するために $DELTVAシステム・サービスを呼び出す場合,$DELTVAシステム・サービスに対して inadr 引数として,retadr 引数に戻されたアドレス範囲 (メモリを割り当てるために使用したルーチンから戻された値) をアプリケーションで使用しているときは, アプリケーションを変更する必要はありません。 実際に割り当てられるサイズは各システムで異なるため, 割り当ての範囲に関してアプリケーションで何らかの仮定を設定することは望ましくありません。


5.3 メモリ・マッピング・ルーチンの確認

アプリケーションで実行するメモリ・マッピングを変更しなければならないかどうかを判断するには, アプリケーションが仮想メモリのどの部分でマッピングを実行するかを確認しなければなりません。 メモリ・マッピング・システム・サービス($CRMPSCと$MGBLSC)を使用すれば, 次の方法でメモリをマッピングできます。

アプリケーションがセクションをマッピングする方法は, おもに$CRMPSCシステム・サービスと $MGBLSCシステム・サービスに対する次の引数によって決定されます。

$CRMPSCシステム・サービスと$MGBLSCシステム・サービスは少なくともCPU固有のページを 1ページ分マッピングします。セクション・ファイルが1ページ未満の場合には, ページの残りの部分には0が格納されます。 ページの残された空間をアプリケーションで使用すべきではありません。なぜなら, セクション・ファイルに格納できるデータだけがディスクに書き戻されるからです。


5.3.1  拡張した仮想アドレス空間へのマッピング

アプリケーションでアプリケーションの仮想アドレス空間の拡張領域にセクション・ ファイルをマッピングする場合には,ソース・コードを変更する必要はありません。 これは,拡張された仮想アドレス空間にマッピングされるため,たとえ Alphaシステムで割り当てられるメモリのサイズがVAXシステムより大きくても, 既存のデータの上にマッピングされる危険性がないからです。このように, VAXシステムで$CRMPSCシステム・サービスに対して引数として指定した値は, Alphaシステムでも正しく機能します。

対処方法

セクションを仮想メモリの拡張領域にマッピングするアプリケーションは, 変更しなくても正しく動作できますが, retadr 引数をアプリケーションで指定していない場合には, この引数を指定することにより, 呼び出しによってマッピングされたメモリの正確な境界を判断するようにしてください。


注意
アプリケーションで relpag 引数を指定する場合には, retadr 引数も指定しなければなりません。これは省略可能な引数ではありません。 relpag 引数の使用についての詳しい説明は, 第5.3.4項を参照してください。

例 5-3は,セクション・ファイルを拡張アドレス 空間にマッピングする$CRMPSCシステム・サービスの呼び出しを示しています。 この例では,次に示すようにDCLのCREATEコマンドを使用して作成された MAPTEST.DATという名前のセクション・ファイルをマッピングします。
     $  CREATE maptest.dat
     test data test data test data test data test data
     test data test data test data test data test data
     test data test data test data test data test data
     test data test data test data test data test data
     test data test data test data test data test data
     test data test data test data test data test data
     test data test data test data test data test data
     test data test data test data test data test data
     [Ctrl/Z]

例 5-3 拡張された仮想アドレス空間へのセクションのマッピング :(クリックで表示)


5.3.2  特定の位置への単一ページのマッピング

アプリケーションでセクション・ファイルを 1ページのメモリにマッピングする場合には,Alphaシステムの$CRMPSCおよび $MGBLSCシステム・サービスでこの操作モードがサポートされないため, ソース・コードを変更しなければなりません。Alphaシステムのページ・サイズは VAXシステムのページ・サイズと異なっており,さらに Alphaアーキテクチャの各実装ごとに異なるため, アプリケーションでセクション・ファイルをマッピングするために正確なメモリ境界を指定しなければなりません。 このような使い方をした場合,$CRMPSCシステム・サービスは, 引数が誤っていることを示すエラー(SS$_INVARG)を戻します。

アプリケーションでこのモードを使用しているかどうかを判断するには, inadr 引数に指定した先頭アドレスと末尾アドレスを確認します。 両方のアドレスが同じであり,同時に,flags 引数の SEC$M_EXPREGビットがセットされていない場合には, アプリケーションはこのモードを使用しています。

対処方法

このモードの$CRMPSCシステム・サービスの呼び出しを変更する場合には, 次のガイドラインに従ってください。


5.3.3 定義されたアドレス範囲へのマッピング

アプリケーションで仮想アドレス空間の定義された領域にセクションをマッピングする場合には, ソース・コードを変更しなければならない可能性があります。これは, Alphaシステムでは$CRMPSCおよび$MGBLSCシステム・サービスが VAXシステムと異なる方法で一部の引数を解釈するからです。相違点は次のとおりです。

対処方法

可能な場合には,拡張された仮想アドレス空間にデータがマッピングされるように, アプリケーションを変更してください。 アプリケーションがデータをマッピングする方法を変更できない場合には, 次のガイドラインに従ってください。

たとえば,例 5-4に示すVAXプログラムは, 第5.3.1項で作成したセクション・ファイルを既存の仮想アドレス空間にマッピングします。 アプリケーションは bufferという名前のバッファを定義します。このバッファのサイズは 512バイトであり,これはVAXのページ・サイズを反映しています。 プログラムはバッファの1バイト目のアドレスを先頭アドレスとして,また, バッファの最終バイトのアドレスを末尾アドレスとして inadr 引数に渡すことにより,セクションの正確な境界を定義します。

例 5-4 仮想アドレス空間の定義された領域へのセクションのマッピング :(クリックで表示)

例 5-4 に示したプログラムをAlphaシステムで正しく実行するには,次の変更が必要です。

これらの目標を達成するための1つの方法として, SOLITARYプログラム・セクション属性を使用することにより, セクション・データを格納したプログラム・セクションを独自のイメージ・セクションに分離する方法があります。

この例では,bufferという名前のセクションは bufferという名前のプログラム・セクション内に示されています (プログラム・セクションの生成方法は, 各プラットフォーム上の言語の種類により異なります。 セクションが独自のプログラム・セクションにあることをコンパイラの解説書で確認してください)。 次のリンク操作は,このプログラム・セクションの SOLITARY属性を設定する方法を示しています。

     $  LINK MAPTEST, SYS$INPUT/OPT
     PSECT_ATTR=BUFFER,SOLITARY
     [Ctrl/Z]
CPU固有のページ境界の末尾にアラインされる末尾アドレスをセクション・バッファに対して指定するには, 実行時にCPU固有のページ・サイズを入手し, その値から1を減算し,その値を使用して配列の最終要素のアドレスを求めます。 この値をinadr引数の2番目のロングワードとして渡します (実行時にページ・サイズを判断する方法については, 第5.4節を参照してください)。 セクションがマッピングされるバッファの割り当てを変更する必要はありません。

アプリケーションが任意のページ・サイズの Alphaシステムで正しく実行されるようにするには, /BPAGE=16修飾子を指定することにより,リンカがイメージ・セクションを 64KBの境界に強制的にアラインするようにします。 実際にマッピングされるメモリの総量は, 使用可能なメモリの合計よりはるかに大きくなる可能性があります。 使用可能なメモリのサイズは,ページ・カウント ( pagcnt )引数の値とセクション・ファイルのサイズのうち, どちらか小さい方の値によって決定されます。 セクションの範囲内に含まれないメモリを使用しないようにするには, retadr 引数に戻された値を使用します。

例 5-5は,Alphaシステムで正しく実行するために 例 5-4に対して必要なソースの変更を示しています。

例 5-5 例 5-4をAlphaシステムで実行するのに必要なソース・コードの変更 :(クリックで表示)

次のリストの各項目は, 例 5-5の番号に対応しています。

【1】ヘッダ・ファイルSYIDEF.Hには, $GETSYIシステム・サービスに対する OpenVMSアイテム・コードの定義が登録されています。
【2】バッファは _align(page)ストレージ記述子を使用せずに定義されています。 ページ・サイズはOpenVMS Alphaシステムで実行するまで判断できないため, DEC C for OpenVMS Alphaコンパイラは,_align(page)が指定されているときに, データをAlphaの最大ページ・サイズ(64KB)にアラインします。
【3】この構造は, 実行時にページ・サイズを入手するために使用される項目リストを定義します。
【4】この変数には, 戻されたページ・サイズ値が格納されます。
【5】 $GETSYIシステム・サービスに対するこの呼び出しでは, 実行時にページ・サイズが入手されます。
【6】バッファの末尾アドレスは, 戻されたページ・サイズ値から1を減算することにより指定されます。


5.3.4  オフセットによるセクション・ファイルのマッピング

アプリケーションではセクション・ファイルの一部だけをマッピングできます。 その場合には, マッピングを開始するアドレスをセクション・ファイルの先頭からのオフセットとして指定します。 このオフセットを指定するには, $CRMPSCシステム・サービスの relpag 引数に対して値を指定します。 relpag 引数の値は, ファイルの先頭を基準にしてマッピングを開始するページ番号を指定します。

$CRMPSCシステム・サービスは互換性を維持するために,VAXシステムと Alphaシステムの両方のシステムにおいて,relpag 引数の値を 512バイト単位で解釈します。しかし,AlphaシステムのCPU固有のページ・サイズは 512バイトより大きいため,relpag 引数にオフセットとして指定する値はおそらく CPU固有のページ境界にアラインされません。$CRMPSCシステム・サービスは仮想メモリを CPU固有のページ単位でのみマッピングできます。したがって, Alphaシステムでは,セクション・ファイルのマッピングはオフセット・アドレスを含む CPU固有のページの先頭から開始され, オフセットによって指定されるアドレスから正確に開始されるわけではありません。


注意
ルーチンは,オフセットによって指定されるアドレスを含む CPU固有のページの先頭からマッピングを開始しますが, retadr 引数に戻される先頭アドレスはオフセットによって指定されたアドレスであり, 実際にマッピングが開始されたアドレスではありません。

アプリケーションでオフセットからセクション・ファイルにマッピングする場合には, Alphaシステムでマッピングされる余分な仮想メモリ空間を格納できるように, inadr 引数に指定されるアドレス範囲のサイズを拡大する必要があります。 指定されるアドレス範囲が小さすぎる場合には, アプリケーションはセクション・ファイルの中で必要な部分全体をマッピングできない可能性があります。 これは,マッピングがセクション・ファイルの先頭アドレスから開始されるからです。

たとえば,VAXシステムでセクション・ファイルをマッピングするときに, ブロック番号15から始まる16ブロックをマッピングする場合には, アドレス範囲として16*512バイトのサイズを inadr 引数に指定し, relpag 引数に対して15を指定できます。これと同じマッピングを Alphaシステムで実行するには,ページ・サイズの違いを考慮しなければなりません。 たとえば,8Kバイト・ページ・サイズのAlphaシステムでは, relpag オフセットによって指定されるアドレスは, 図 5-2に示すように,15ページレットを CPU固有の1ページに格納できます。Alphaシステムでは, $CRMPSCシステム・サービスはセクション・ファイルのマッピングを CPU固有のページ境界から開始するため,16番目から 30番目までのブロックを正しくマッピングできません。 マッピングを正しく実行するには,Alphaシステムで$CRMPSCシステム・サービス (または$MGBLSCシステム・サービス)がマッピングする追加の 15ページレットを格納できるようにアドレス範囲のサイズを拡大しなければなりません。 このようにサイズを拡大しなかった場合には,指定したセクション・ファイルの中で 1ブロックだけしかマッピングされません。 図 5-2はこの状況を示しています。

図 5-2 オフセットによるマッピングに対してアドレス範囲が与える影響 :(クリックで表示)

relpag 引数に指定するアドレス範囲をどれだけ拡大するかを計算する場合には, 次の公式を使用すると便利です。この公式は, 特定の数のページレットをマッピングするのに充分な CPU固有のページ数を計算します。
     (number_of_pagelets_to_map + (2*pagelets_per_page) - 2) 
                       pagelets_per_page
たとえば,この公式を使用すれば, 前の例に指定したアドレス範囲をどれだけ拡大すればよいかを計算できます。 次の式では,ページ・サイズは8Kであると仮定しています。したがって, pagelets_per_page は16になります。
     16+((2x16)-2)/16=2.87...
結果をもっとも近い整数に切り捨てることにより,この公式は inadr 引数に指定するアドレス範囲が,CPU固有のページの 2ページに対応しなければならないことを示しています。


5.4 ページ・サイズの実行時確認

Alphaシステムでサポートされるページ・サイズを確認するには, $GETSYIシステム・サービスを使用します。例 5-6は, このシステム・サービスを使用して実行時にページ・サイズを確認する方法を示しています。

例 5-6  CPU固有のページ・サイズを確認するための$GETSYIシステム・サービスの使用

     #include  <ssdef.h>
     #include  <stdio.h>
     #include  <stsdef.h>
     #include  <descrip.h>
     #include  <dvidef.h>
     #include  <rms.h>
     #include  <secdef.h>
     #include  <syidef.h>  /* defines page size item code symbol */

     struct itm {               /* define item list                       */
         short int     buflen;  /* length in bytes of return value buffer */
         short int  item_code;  /* item code                              */
         long          bufadr;  /* address of return value buffer         */
         long       retlenadr;  /* address of return value length buffer  */
       } itmlst[2];

     long  cpu_pagesize;
     long  cpu_pagesize_len;

     main( argc, argv )
     int argc;
     char *argv[];
     {
          int    status = 0;

          itmlst[0].buflen =  4;                /*  page size requires 4 bytes  */
          itmlst[0].item_code = SYI$_PAGE_SIZE; /*  page size item code         */
          itmlst[0].bufadr =  &cpu_pagesize;    /*  address of ret_val buffer   */
          itmlst[0].retlenadr = &cpu_pagesize_len; /* addr of length of ret_val */
          itmlst[1].buflen = 0;
          itmlst[1].item_code = 0;   /* Terminate item list with longword of 0  */

          status = sys$getsyiw( 0, 0, 0, &itmlst, 0, 0, 0 );

          if( status & STS$M_SUCCESS )
          {
               printf("getsyi succeeds, page size = %d\n",cpu_pagesize);
               exit( status );
          }
          else
          {
               printf("getsyi fails\n");
               exit( status );
          }
     }


5.5  メモリをワーキング・セットとしてロックする操作

$LKWSETシステム・サービスは,VAXシステムでもAlphaシステムでも同様に, アドレス範囲として inadr 引数に指定したページ範囲をワーキング・ セットとしてロックします。このシステム・サービスは必要に応じて,アドレスを CPU固有のページ境界に調整します。

しかし,Alpha命令は完全な仮想アドレスを指定できないため, Alphaイメージはプロシージャ記述子に対するポインタを通じて, 間接的にプロシージャとデータを参照しなければなりません。 プロシージャ記述子には,実際のコード・アドレスも含めて, プロシージャに関する情報が格納されます。 プロシージャ記述子とデータに対するこれらのポインタは, リンケージ・セクションと呼ぶ新しいプログラム・セクションに収集されます。

対処方法

Alphaシステムでは, 単にコード・セクションをメモリにロックするだけでは性能を向上するのに不十分です。 関連するリンケージ・セクションもワーキング・セットとしてロックしなければなりません。

リンケージ・セクションをメモリ内でロックするには, リンケージ・セクションの先頭アドレスと末尾アドレスを判断し, $LKWSETシステム・サービスを呼び出すときに, inadr 引数の値としてこれらのアドレスを指定しなければなりません。


[ 前のページ ] [ 次のページ ] [ 目次 ] [ 索引 ] [ DOC Home ]