Memory Channel アプリケーション・プログラミング・インタフェース (API) を使うと,自動エラー処理,ロック,および UNIX スタイルの保護を使用して,Memory Channel API クラスタ・メンバ間で効率のよいメモリ共用が実現できます。この章では,Memory Channel API ライブラリをベースにしたアプリケーションの開発に役立つ情報を説明します。また,Memory Channel のアドレス空間と,一般的な共用メモリとの違いを説明し,トランスポート層の機構として,Memory Channel を使うプログラミングと,共用メモリを使うプログラミングでは,どのように異なるかを説明します。
注意
Memory Channel API ライブラリは,Memory Channel ハードウェアで構成されたクラスタでだけサポートされます。Memory Channel ハードウェアが実装されていないクラスタでは,アプリケーションはクラスタ単位でのキル・メカニズム (
と kill
(1)を参照) を使用する場合があります。このメカニズムは Memory Channel API の 1 つの機能の代わりとして使うことができます。 kill
(2)
この章では,プログラムで Memory Channel API ライブラリの関数を使用する方法についても,例を使って説明します。このプログラムのソース・ファイルは,/usr/examples/cluster/
ディレクトリにあります。各ファイルには,コンパイルの指示が記載されています。
この章では,次のトピックについて説明します。
Memory Channel のマルチレール・モデルの理解 (10.1 節)
Memory Channel API ライブラリの初期化 (10.2 節)
ユーザ・プログラムでの Memory Channel API ライブラリの初期化 (10.3 節)
Memory Channel の構成のチューニング (10.4 節)
トラブルシューティング (10.5 節)
Memory Channel のアドレス空間へのアクセス (10.6 節)
クラスタ単位のロックの使用 (10.7 節)
クラスタ・シグナルの使用 (10.8 節)
クラスタ情報へのアクセス (10.9 節)
共用メモリ・モデルとメッセージ渡しモデルの比較 (10.10 節)
Memory Channel API を使って TruCluster Server システム用のプログラムを開発しているプログラマからの質問と,それに対する回答 (10.11 節)
10.1 Memory Channel マルチレール・モデル
Memory Channel マルチレール・モデルには,物理レールと論理レールの概念があります。物理レールとは,ケーブルおよび Memory Channel アダプタが接続された Memory Channel ハブ (物理または仮想) と,各ノードのアダプタ用の Memory Channel ドライバのことをいいます。論理レールは,1 つまたは 2 つの物理レールで構成されます。
1 つのクラスタは,1 つ以上,最大 4 つまでの論理レールを持つことができます。論理レールは,次のスタイルで構成することができます。
クラスタがシングルレール・スタイルで構成されている場合,物理レールと論理レールは 1 対 1 に対応します。この構成では,フェイルオーバには対応できません。つまり,物理レールに障害が起きた場合,論理レールにも障害が発生することになります。
シングルレール構成を使う利点は,アプリケーションがすべての論理レールのアドレス空間の集合体にアクセスでき,集合体の帯域幅を利用して最大限の性能を実現できることです。
図 10-1
は,3 つの論理レールからなるシングルレールの Memory Channel の構成を示します。各論理レールには,それぞれ物理レールが対応しています。
図 10-1: シングルレール Memory Channel の構成
クラスタがフェイルオーバ・ペア・スタイルで構成されている場合,1 つの論理レールは 2 つの物理レールからなっており,一方がアクティブ状態で,もう一方は非アクティブ状態になっています。アクティブ状態の物理レールが故障した場合,フェイルオーバが起こり,非アクティブ状態だった物理レールが使われます。これによって,フェイルオーバ後も,論理レールはアクティブ状態を保てます。このフェイルオーバは,ユーザからは見えません。
フェイルオーバ・ペア・スタイルは,Memory Channel が 2 つの物理レールで構成されている場合にのみ,使うことができます。
フェイルオーバ・ペア構成では,2 番目の物理レールを予備として使えるため,物理レールに障害が発生した場合の可用性が高くなります。ただし,一度に利用できるアドレス空間と帯域幅は,どちらか一方の物理レールのものだけです。
図 10-2
は,フェイルオーバ・ペア・スタイルのマルチレール Memory Channel の構成を示しています。この図の構成では,2 つの物理レールから 1 つの論理レールができています。
図 10-2: フェイルオーバ・ペア Memory Channel の構成
10.1.3 Memory Channel のマルチレール・モデルの構成
Memory Channel のマルチレール・モデルを実現する場合,クラスタ内のすべてのノードは,同じ数の物理レールで同じ数の論理レールを構成し,それぞれが同じフェイルオーバ・スタイルでなければなりません。
ファイル
/etc/sysconfigtab
内のシステム構成パラメータ
rm_rail_style
を使って,マルチレール・スタイルを設定します。rm_rail_style
パラメータには,次の値のどちらかが設定されます。
シングルレール・スタイルの場合は 0。
フェイルオーバ・ペア・スタイルの場合は 1。
rm_rail_style
パラメータの省略時の値は 1 です。
rm_rail_style
パラメータは,クラスタ内のすべてのノードで同じ値でなければなりません。値が異なっていると,構成エラーが発生することがあります。
rm_rail_style
パラメータの値を 0 に変更してシングルレール・スタイルにするには,/etc/sysconfigtab
ファイルを変更して,rm
サブシステムのスタンザを,次のように追加または変更してください。
rm: rm_rail_style=0
注意
/etc/sysconfigtab
ファイル内のスタンザを修正または追加する場合は,を使用することをお奨めします。 sysconfigdb
(8)
rm_rail_style
パラメータを変更した場合は,クラスタ全体を停止し,各メンバ・システムをリブートしなければなりません。
Memory Channel のマルチレール・モデルのエラー処理は,指定した論理レールに対して実行されます。Memory Channel API ライブラリのエラー処理関数の説明とコーディング例は,10.6.6 項を参照してください。
注意
Memory Channel のマルチレール・モデルを使うと,ハブや Memory Channel アダプタの追加といったクラスタの再構成が難しくなります。このような再構成を行う場合は,クラスタを完全にシャットダウンしなければなりません。
10.2 Memory Channel API ライブラリの初期化
Memory Channel API ライブラリをベースにしたアプリケーションを実行するには,Memory Channel API クラスタの各ホストでライブラリを初期化しなければなりません。imc_init
コマンドで Memory Channel API ライブラリを初期化し,アプリケーションが API を使用できるようにします。Memory Channel API ライブラリの初期化は,システムのブート時に
imc_init
コマンドを自動的に実行して行う場合と,システムのブート後に,システム管理者がコマンド行からこのコマンドを実行して行う場合があります。
システムのブート時に Memory Channel API ライブラリを初期化するかどうかは,/etc/rc.config
ファイル内の
IMC_AUTO_INIT
変数で制御します。この変数が 1 に設定されている場合は,システムのブート時に
imc_init
コマンドが呼び出されます。Memory Channel API ライブラリがブート時に初期化される場合,-a
maxalloc
フラグおよび
-r
maxrecv
フラグの値には,/etc/rc.config
ファイル内の変数
IMC_MAX_ALLOC
および
IMC_MAX_RECV
に指定された値が設定されます。maxalloc
パラメータおよび
maxrecv
パラメータの省略時の値は 10 MB です。
IMC_AUTO_INIT
変数が 0 に設定されている場合,Memory Channel API ライブラリは,システムのブート時に初期化されません。システム管理者は,imc_init
コマンドを実行してライブラリを初期化しなければなりません。imc_init
コマンドを手動で実行した場合は,/etc/rc.config
ファイルのパラメータ値は使用されません。
imc_init
コマンドは,システムのブート時と,システムのブート後のどちらで実行されたかにかかわらず,最初に実行されたときに Memory Channel API ライブラリを初期化します。-a
maxalloc
フラグの値は,Memory Channel API クラスタ内では,すべてのホストで同じでなければなりません。異なる値が指定されている場合は,指定されている値のうち,クラスタ内で最も大きな値がすべてのホストに適用されます。
Memory Channel API ライブラリが現在のホストで初期化された後,システム管理者は
imc_init
コマンドを再び実行して,リソースの限界値
maxalloc
および
maxrecv
を再構成することができます。このとき,リブートする必要はありません。システム管理者はどちらの限界値も増減させることができますが,新しい限界値を現在のリソース使用量よりも小さくすることはできません。コマンド行からクラスタを再構成しても,/etc/rc.config
ファイルに指定されている値を読み取ることや,この値を変更することはできません。システム管理者は
rcmgr
(8)
imc_init
コマンドを実行するには,ルート特権が必要です。
10.3 ユーザ・プログラムでの Memory Channel API ライブラリの初期化
imc_api_init
関数は,ユーザ・プログラム内で Memory Channel API ライブラリを初期化するために使用します。プロセスで,imc_api_init
関数を呼び出した後にこれ以外の Memory Channel API の関数を呼び出します。プロセスが子プロセスを生成 (fork) する場合,子プロセスでは,imc_api_init
関数を呼び出した後にこれ以外の Memory Channel API の関数を呼び出さなければなりません。この順番で呼び出さなければ,予期しない動作をします。
10.4 Memory Channel 構成のチューニング
imc_init
コマンドは,省略時の設定で指定されるリソースで Memory Channel API ライブラリを初期化します。アプリケーションによっては,省略時の設定より多くのリソースが必要になるかもしれません。場合によっては,Memory Channel のパラメータと仮想メモリのリソース・パラメータの値を変更して,これらの制限を超えることができます。以降の各項では,これらのパラメータについて説明し,これらの変更方法も示します。
10.4.1 Memory Channel のアドレス空間を拡張する
Memory Channel API ライブラリで利用できる Memory Channel のアドレス空間の総容量は,imc_init
コマンドの
maxalloc
パラメータで指定します。ホストが受信用にアタッチできる Memory Channel のアドレス空間の最大容量は,imc_init
コマンドの
maxrecv
パラメータで指定します。省略時の制限は,それぞれ 10 MB です。10.2 節で,imc_init
コマンドを使って Memory Channel API ライブラリを初期化する方法を説明しています。
自動的に初期化するときに使用される値を変更したい場合は,
rcmgr
(8)IMC_MAX_ALLOC
および
IMC_MAX_RECV
の値を設定することにより行うことができます。たとえば,Memory Channel API ライブラリのクラスタ単位で利用できる Memory Channel のアドレス空間として,全部で 80 MB を割り当て,このうち 60 MB を,現在のホストが受信用にアタッチする領域として割り当てるには,次のようにこの変数を設定します。
rcmgr set IMC_MAX_ALLOC 80 rcmgr set IMC_MAX_RECV 60
rcmgr
(8)
Memory Channel API ライブラリが初期化された後に,利用可能な Memory Channel のアドレス空間の総容量,および受信用にアタッチできる Memory Channel のアドレス空間の最大容量はどちらも,Memory Channel API ライブラリの初期化コマンド
imc_init
を使って変更することができます。たとえば,クラスタ単位で利用可能な Memory Channel のアドレス空間として全部で 80 MB を割り当て,現在のホストで受信用にアタッチする Memory Channel のアドレス空間に 60 MB を割り当てる場合は,次のコマンドを実行します。
imc_init -a 80 -r 60
ただし,imc_init
コマンドを使って新しい限界値を設定した場合,設定した値はシステムのリブート時に消滅し,変数
IMC_MAX_ALLOC
および
IMC_MAX_RECV
の値が限界値として再び使われるようになります。
10.4.2 固定メモリを増やす
受信用にアタッチする Memory Channel のアドレス空間の各ページは,システムの物理メモリのページに対応していなければなりません。このメモリは,ページング不可のメモリ,つまり固定メモリです。ホスト上の固定メモリは,無限に増やすことはできません。システム構成パラメータ
vm_syswiredpercent
で制限されます。/etc/sysconfigtab
ファイル内の
vm_syswiredpercent
パラメータを変更することができます。
たとえば,vm_syswiredpercent
パラメータに 80 を設定する場合,/etc/sysconfigtab
ファイルの
vm
スタンザに,次のようなエントリを記述する必要があります。
vm: vm_syswiredpercent=80
vm_syswiredpercent
パラメータを変更した場合は,システムをリブートしなければなりません。
注意
大半の運用形態では,省略時の固定メモリの容量で十分に対応できます。この限界値を変更する場合は十分に注意するようにしてください。
以降の各項では,Memory Channel API ライブラリの関数を使った場合に発生するエラー状況について説明し,解決策を示します。
10.5.1 IMC_NOTINIT リターン・コード
IMC_NOTINIT
状態は
imc_init
コマンドがまだ実行されていない場合,または
imc_init
コマンドが正常に終了していない場合に返されます。
Memory Channel API ライブラリの関数を使用する前に,imc_init
コマンドを Memory Channel API クラスタ内の各ホスト上で実行しなければなりません。10.2 節で,imc_init
コマンドを使って Memory Channel API ライブラリを初期化する方法を説明しています。
imc_init
コマンドが正常に終了しなかった場合,考えられる解決方法については,10.5.2 項を参照してください。
10.5.2 Memory Channel API ライブラリの初期化の失敗
ホスト上で Memory Channel API ライブラリの初期化に失敗することがあります。このような場合は,エラー・メッセージがコンソールに表示されるとともに,/usr/adm
ディレクトリの
messages
ログ・ファイルに書き込まれます。次に示す一連のエラー・メッセージとその解決方法を参考にして,エラーを取り除いてください。
Memory Channel is not initialized for user access
このエラー・メッセージは,現在のホストが Memory Channel API を使うように初期化されていないことを示します。
この問題を解決するには,Memory Channel のすべてのケーブルが,このホストの Memory Channel のアダプタに正しく接続されていることを確認してください。
Memory Channel API - insufficient wired memory
このエラー・メッセージは,/etc/rc.config
ファイルの
IMC_MAX_RECV
変数の値または
imc_init
コマンドで指定した
-r
オプションの値が,構成パラメータ
vm_syswiredpercent
で指定されている固定メモリの限界値よりも大きいことを示しています。
この問題を解決するには,maxrecv
パラメータにもっと小さい値を指定して,imc_init
コマンドを呼び出すか,10.4.2 項で説明しているようにして,システムの固定メモリの限界値を大きくします。
10.5.3 Memory Channel の致命的なエラー
Memory Channel API を初期化する際に,Memory Channel の物理的な構成やインターコネクトが原因で障害が発生することがあります。こうした状況で,コンソールに表示されるエラー・メッセージでは,Memory Channel API のことには触れていません。次の項で,このような障害が発生する典型的な原因を挙げます。
10.5.3.1 レールの初期化の失敗
ノードのレールの構成が,他のクラスタ・メンバのものと同じでない場合,そのクラスタ内のいくつかのホストで,システム・パニックが発生し,コンソールに次の形式のエラー・メッセージが表示されます。
rm_slave_init rail configuration does not match cluster expectations for rail 0 rail 0 has failed initialization rm_delete_context: lcsr = 0x2a80078, mcerr = 0x20001, mcport = 0x72400001 panic (cpu 0): rm_delete_context: fatal MC error
このエラーは,構成パラメータ
rm_rail_style
がすべてのノードで同じでない場合に発生します。
この問題を解決するには,次の手順を実行してください。
システムを停止します。
/genvmunix
をブートします。
10.1.3 項の説明に従い,/etc/sysconfigtab
ファイルを修正します。
Memory Channel API クラスタ・サポート (/vmunix
) を使ってカーネルをリブートします。
ある操作を実行するのに Memory Channel のアドレス空間が不足していると,IMC_MCFULL
状態が返されます。
Memory Channel API ライブラリで利用できる Memory Channel のアドレス空間の総容量は,10.5.2 項で説明しているように,imc_init
コマンドの
maxalloc
パラメータで指定します。
rcmgr
(8)imc_init
を使って,ライブラリがクラスタ単位で利用できる Memory Channel のアドレス空間を増やすこともできます。
詳細は,10.4.1 項を参照してください。
クラスタ内の Memory Channel のアドレス空間が十分でない場合,ブートしているノードはクラスタへの結合に問題が生じていることがあります。その場合には,1 つ以上のメンバが妥当性チェックの失敗でパニックになっている (ICS MCT Assertion Failed
) か,またはブートしているメンバがブート初期にハングしている場合があります。
Memory Channel リソースは,新しいメンバがクラスタのメンバになるときに動的に割り当てられます。Memory Channel アプリケーション・プログラミング・インタフェース (API) のライブラリ関数を呼び出すアプリケーションが実行されていると,必要な Memory Channel リソースを消費し,メンバはクラスタのメンバになるのに必要なリソースを取得できなくなる場合があります。この問題を解決するには,Memory Channel API ライブラリ関数を呼び出すアプリケーションを開始する前にすべてのクラスタ・メンバをブートしてください。
10.5.5 IMC_RXFULL リターン・コード
ある領域を受信用にアタッチしようとしたときに,受信用のマッピング空間を使いきっていた場合,imc_asattach
関数は
IMC_RXFULL
状態を返します。
注意
現在のホスト上での受信用空間の省略時の総容量は,10 MB です。
各ホスト上で受信用にアタッチできる Memory Channel のアドレス空間の最大容量は,10.2 節で説明しているように,imc_init
コマンドの
maxrecv
パラメータを使って指定します。
rcmgr
(8)imc_init
を使って,ホスト上で受信用にアタッチできる Memory Channel のアドレス空間の最大容量を拡張することができます。詳細は,10.4.1 項を参照してください。
10.5.6 IMC_WIRED_LIMIT リターン・コード
リターン値
IMC_WIRED_LIMIT
は,固定メモリの最大容量を超える操作が行われたことを示します。
固定メモリの限界値は,システム構成パラメータ
vm_syswiredpercent
で指定します。この限界値の変更方法についての詳細は,10.4.2 項を参照してください。
10.5.7 IMC_MAPENTRIES リターン・コード
リターン値
IMC_MAPENTRIES
は,現在のプロセスの仮想メモリのマップ・エントリの数が,最大値を超えたことを示します。
10.5.8 IMC_NOMEM リターン・コード
リターン状態
IMC_NOMEM
は,malloc
関数が Memory Channel API の関数を呼び出した際に失敗したことを示します。
これは,プロセスの仮想メモリの限界値を超えた場合に発生し,プロセスの仮想メモリの上限を増やす通常の方法で解決することができます。つまり,C シェルの場合は
limit
コマンドおよび
unlimit
コマンドを使い,Bourne シェル,および Korn シェルの場合は
ulimit
コマンドを使って解決します。
10.5.9 IMC_NORESOURCES リターン・コード
IMC_NORESOURCES
リターン値は,要求された動作を実行するのに利用できる Memory Channel のデータ構造体が不足していることを示します。ただし,利用可能な Memory Channel のデータ構造体の大きさは固定であり,パラメータを変更して増やすことはできません。この問題を解決するには,使用する領域やロックを減らすようにアプリケーションを修正する必要があります。
10.6 Memory Channel のアドレス空間へのアクセス
Memory Channel インターコネクトは,Memory Channel API クラスタ・メンバ間でのメモリ共用の 1 つの形態です。Memory Channel API ライブラリを使ってメモリ共用をセットアップすると,クラスタ内の各メンバ上のプロセスは,その仮想アドレス空間のアドレスに直接読み取り/書き込み操作を実行するだけで,互いのデータを交換することができます。Memory Channel API ライブラリを使ってメモリ共用をセットアップすると,これらの直接の読み取り/書き込み操作は,オペレーティング・システムや Memory Channel API ライブラリ・ソフトウェアの関数を介さずに,ハードウェアの速度で実行されます。
システムが Memory Channel を使うように構成されている場合,システムの物理アドレス空間の一部が Memory Channel のアドレス空間に割り当てられます。Memory Channel のアドレス空間の大きさは,imc_init
コマンドで指定します。プロセスは,Memory Channel API を使って,Memory Channel のアドレス空間の領域をプロセスの仮想アドレス空間にマップすることによって,この Memory Channel のアドレス空間にアクセスします。
別のクラスタ・メンバ上の Memory Channel のアドレス空間にアクセスするアプリケーションでは,imc_asalloc
関数を呼び出して,アドレス空間の一部を特定の目的のために割り当てることができます。key
パラメータで,クラスタ単位のキーと領域を関連付けます。同じ領域を割り当てる他のプロセスは,これと同じキーを指定します。こうすることで,複数のプロセスがこの領域に連携してアクセスできるようになります。
プロセスは,Memory Channel のアドレス空間の割り当てられた領域を使うために,imc_asattach
関数または
imc_asattach_ptp
関数を使って,その領域をそのプロセスの仮想アドレス空間にマップします。プロセスが Memory Channel の領域にアタッチすると,アタッチした領域と同じ大きさの領域が,プロセスの仮想アドレス空間に追加されます。領域をアタッチする際に,プロセスはその領域をデータ送信用にマップするか,データ受信用にマップするかを次のように指定します。
送信 -- その領域が Memory Channel を介したデータ送信に使われることを示します。プロセスがこの仮想アドレス領域のアドレスに書き込みを行うと,データは Memory Channel インターコネクトを通して Memory Channel API クラスタの他のメンバに送信されます。
送信用に領域をマップするには,imc_asattach
関数の
dir
パラメータに,値
IMC_TRANSMIT
を指定します。
受信 -- その領域が Memory Channel からのデータの受信に使われることを示します。この場合,プロセスの仮想アドレス空間にマップされるアドレス空間は,実際にはそのシステム上の物理メモリの領域に対応します。データが Memory Channel 上で送信されると,そのデータは,受信用としてその領域をマップしているすべてのホストの物理メモリに書き込まれます。これにより,そのシステム上の複数のプロセスが,物理メモリの同じ領域からこのデータを読み取れるようになります。プロセスは,この領域をマップする前に送信されたデータを受信することはできません。
受信用に領域をマップするには,imc_asattach
関数の
dir
パラメータに,値
IMC_RECEIVE
を指定します。
Memory Channel インターコネクトを使ってメモリを共用する方式は,接続を確立すれば,仮想アドレス空間にアクセスするだけで,2 つの異なるプロセスがデータを共用できるという点では,通常の共用メモリと似ています。ただし,これらのメモリ共用方式には,次のような考慮しなければならない違いが 2 つあります。
通常の共用メモリでは,作成したときに仮想アドレスが割り当てられます。C プログラムの用語では,メモリへのポインタがあるということです。この 1 つのポインタは,その共用メモリの読み取りにも書き込みにも使うことができます。一方,Memory Channel の領域には,2 つの異なる仮想アドレス,つまり送信用仮想アドレスと受信用仮想アドレスを割り当てることができます。C プログラムの用語で言えば,2 つの異なるポインタを管理するということです。1 つのポインタは,書き込み操作でのみ使用し,もう一方は読み取り操作で使用します。
通常の共用メモリでは,書き込み操作は直接メモリに対して実行されるため,その結果は同じメモリを読み取っている他のプロセスにすぐに反映されます。しかし,Memory Channel の領域に書き込み操作を行う場合は,書き込みは直接メモリに対して行われるのではなく,入出力システムと Memory Channel のハードウェアに対して行われます。したがって,受信側のシステム上のメモリにデータが届くまでに遅れが生じます。これについては,10.6.5 項で詳しく説明します。
10.6.1 Memory Channel のアドレス空間へのアタッチ
以降の各項では,プロセスに Memory Channel のアドレス空間をアタッチさせる方法を説明します。プロセスに Memory Channel のアドレス空間をアタッチさせる方法は,次のとおりです。
ブロードキャスト・アタッチ (10.6.1.1 項)
ポイント・ツー・ポイント・アタッチ (10.6.1.2 項)
ループバック・アタッチ (10.6.1.3 項)
この節では,初期状態の一貫性,Memory Channel 領域の読み取り/書き込み,遅延と一貫性,およびエラー管理についても説明し,コーディング例をいくつか示します。
10.6.1.1 ブロードキャスト・アタッチ
あるプロセスが送信用にマップした領域を,別のプロセスが受信用にマップした場合,送信側プロセスがその領域に書き込んだデータは Memory Channel を通して,もう一方のプロセスの受信メモリに伝送されます。図 10-3
は,ホストが 3 つある Memory Channel システムで,アドレス空間がどのように対応するかを示します。
図 10-3: ブロードキャスト・アドレス空間のマッピング
上記の図 10-3 のようにアドレス空間をマッピングすると,次のようになります。
プロセス A は,Memory Channel のアドレス空間のある領域を割り当てます。次に,このプロセス A は,imc_asattach
関数を使ってこの領域を送信用にアタッチして,この領域を自分の仮想アドレス空間にマップします。
プロセス B と C は,どちらもプロセス A と同じ Memory Channel のアドレス空間の領域を割り当てます。ただし,プロセス A とは異なり,プロセス B と C は,データを受信するためにこの領域にアタッチします。
データがプロセス A の仮想アドレス空間に書き込まれると,そのデータは Memory Channel 上で伝送されます。
プロセス A からのデータが Memory Channel に送られると,このデータは,プロセス B および C の,データ受信用に割り当てられた仮想アドレス空間に対応する物理メモリに書き込まれます。
割り当てた Memory Channel のアドレス空間の領域は,別のノード上の特定のプロセスの仮想アドレス空間に,送信用としてポイント・ツー・ポイント・モードでアタッチすることができます。これは,パラメータでホストを指定し,imc_asattach_ptp
関数を呼び出すことで実行できます。これにより,この領域に書き込んだデータは,クラスタ内のすべてのホストに送られるのではなく,パラメータで指定したホストにのみ送られるようになります。
imc_asattach_ptp
関数を使ってアタッチする領域は,常に送信モードでアタッチし,書き込みのみが可能です。図 10-4
は,ホストが 2 つある Memory Channel システムで,ポイント・ツー・ポイントのアドレス空間がどのように対応するかを示します。
図 10-4: ポイント・ツー・ポイント・アドレス空間のマッピング
上記の図 10-4 のようにアドレス空間をマッピングすると,次のようになります。
プロセス 1 は,Memory Channel のアドレス空間の領域を割り当てます。次に,imc_asattach_ptp
関数を使って,この領域をホスト B とのポイント・ツー・ポイント用としてアタッチすることで,この領域を自分の仮想アドレス空間にマップします。
プロセス 2 は,この領域を割り当て,imc_asattach
関数を使って,受信用としてこの領域にアタッチします。
データがプロセス 1 の仮想アドレス空間に書き込まれると,このデータは Memory Channel 上で伝送されます。
プロセス 1 からのデータが Memory Channel に送られると,このデータは,ホスト B 上のプロセス 2 の仮想アドレス空間に対応する物理メモリに書き込まれます。
Memory Channel の領域は,1 つのホスト上のプロセスから送信と受信の両方でアタッチすることができます。あるホストが書き込んだデータは,その領域を受信用にアタッチしている他のホストに書き込まれます。しかし,省略時の設定では,あるホストが書き込んだデータは,そのホストの受信用メモリには書き込まれないようになっています。データは,他のホストにのみ書き込まれます。書き込んだデータを,書き込んだホスト自身で参照できるようにするには,送信用にアタッチする際に,imc_asattach()
関数に
IMC_LOOPBACK
フラグを指定しなければなりません。
領域のループバック属性はホスト単位でセットアップし,そのホストでの最初の送信用アタッチで渡す
flag
パラメータの値で判断されます。
flag
パラメータに値
IMC_LOOPBACK
を指定すると,書き込みのたびに 2 つの Memory Channel トランザクションが発生します。1 つはデータの書き込み用で,もう 1 つはデータのループバック用です。
ポイント・ツー・ポイント・アタッチ方式では,性質上,ループバックによる書き込みは許されません。
図 10-5
では,Memory Channel のアドレス空間の領域を,ループバック付きの送信と受信の両方でアタッチする場合の構成を示します。
図 10-5: ループバック・アドレス空間のマッピング
Memory Channel の領域に受信用にアタッチした場合,初期状態は不定です。このような状況は,Memory Channel のある領域を送信用にマップしているプロセスが,他のプロセスがその領域を受信用にマップする前に,その領域の内容をアップデートした場合に発生します。これは,初期状態の一貫性の問題と呼ばれます。この問題に対処する方法は 2 つあります。
アプリケーションを作成する際に,受信側のすべてのプロセスが領域にアタッチした後で,送信側のプロセスが領域に書き込むようにする。
imc_asalloc
関数を使って領域を割り当てる際に,IMC_COHERENT
フラグを指定して,その領域の一貫性を保つことを明示する (一貫性のある領域)。これによって,その領域へのアップデート内容は,プロセスがその領域にアタッチするタイミングに関係なく,すべてのプロセスに反映されるようになる。
一貫性のある領域では,ループバック機能を使っています。したがって,書き込みを 1 回行うたびに,データ書き込みとデータのループバックのための 2 つの Memory Channel トランザクションが発生します。このため,一貫性のある領域では,利用可能な帯域幅が,一貫性のない領域よりも小さくなります。
10.6.3 Memory Channel 領域の読み取りと書き込み
Memory Channel のアドレス空間の領域をアタッチするプロセスは,送信ポインタにのみ書き込むことができ,受信ポインタからのみ読み取ることができます。送信ポインタから読み取ろうとすると,セグメント違反になります。
Memory Channel の送信ポインタからの明らかな読み取り操作以外に,コンパイラが読み取り,変更,書き込みのサイクルの命令を生成するような操作でも,セグメント違反が発生します。たとえば,次のような操作です。
後置インクリメントおよび後置デクリメント演算。
前置インクリメントおよび前置デクリメント演算。
4 バイトの整数倍バイトでない単純データ型への代入。
bcopy
(3)length
パラメータが 8 バイトの整数倍でない場合,または,コピー元またはコピー先として渡す引数が 8 バイトの境界に整列されていない場合。
クォドワード (quadword) に整列されていない構造体,つまり
sizeof
関数でのリターン値が 8 の整数倍でない構造体への代入。これは,構造体全体を 1 単位として代入する場合にのみあてはまる。たとえば,mystruct1 = mystruct2
のような場合。
例 10-1 では,Memory Channel のアドレス空間の領域の初期化,割り当て,およびこれへのアタッチの方法を具体的に示します。また,次の 2 つの点について,Memory Channel のアドレス空間と通常の共用メモリの違いを示します。
例 10-1 に示すサンプル・プログラムは,コマンド行のパラメータで,マスタ・モードまたはスレーブ・モードを指定して実行します。マスタ・モードでは,Memory Channel のグローバルなアドレス空間のデータ構造体に,プログラムのプロセス識別子 (PID) を書き込みます。スレーブ・モードでは,Memory Channel のアドレス空間のデータ構造体をポーリングして,マスタ・プロセスの PID を確認します。
注意
個々のアプリケーションでは,キーに対する命名スキームを定義する必要があります。プログラムでは,キーの衝突による問題に対応できるように,キーを柔軟に使用する必要があります。アプリケーション固有の,意味のあるキーを使用してください。
アドレス空間の例は,/usr/examples/cluster/mc_ex1.c
にあります。コンパイル後に,1 つのクラスタ・メンバ上でスレーブ (mc_ex1 0
) を起動し,次に別のクラスタ・メンバ上でマスタ (mc_ex1 1
) を起動します。
例 10-1: Memory Channel のアドレス空間の領域へのアクセス
/* * To compile: cc -g -o mc_ex1 mc_ex1.c -limc */ #include <c_asm.h> #include <sys/types.h> #include <sys/imc.h> #define mb() asm("mb") #define VALID 756 main (int argc, char *argv[]) { imc_asid_t glob_id; typedef struct { pid_t pid; volatile int valid; [1] } clust_pid; clust_pid *global_record; caddr_t add_rx_ptr = 0, add_tx_ptr = 0; int status; int master; int logical_rail=0; char *prog; prog = argv[0]; /* check for correct number of arguments */ if (argc != 2) { printf("usage: %s 0|1\n", prog); exit(-1); } /* test if process is master or slave */ master = atoi(argv[1]); [2] /* initialize Memory Channel API library */ status = imc_api_init(NULL); [3] if (status < 0) { imc_perror("imc_api_init",status); [4] exit(-2); } imc_asalloc(123, 8192, IMC_URW, 0, &glob_id, logical_rail); [5] if (master) { imc_asattach(glob_id, IMC_TRANSMIT, IMC_SHARED, 0, &add_tx_ptr); [6] global_record = (clust_pid*)add_tx_ptr; [7] global_record->pid = getpid(); mb(); [8] global_record->valid = VALID; mb(); } else { /* secondary process */ imc_asattach(glob_id, IMC_RECEIVE, IMC_SHARED, 0, &add_rx_ptr); [9] (char*)global_record = add_rx_ptr; while ( global_record->valid != VALID) ; /* continue polling */ [10] printf("pid of master process is %d\n", global_record->pid); } imc_asdetach(glob_id); imc_asdealloc(glob_id); [11] }
valid
フラグを volatile として定義することで,コンパイラの最適化によって,アップデートした PID の値がメモリから読み取れなくなるのを防いでいます。
[例に戻る]
コマンド行の最初の引数で,プロセスがマスタ・プロセス (引数は 1) か,スレーブ・プロセス (引数は 0) かを指定します。 [例に戻る]
imc_api_init
関数は,Memory Channel API ライブラリを初期化します。この関数は,Memory Channel API ライブラリの他の関数を呼び出す前に呼び出さなければなりません。
[例に戻る]
すべての Memory Channel API ライブラリの関数は,正常に終了した場合,リターン状態として 0 を返します。imc_perror
関数は,エラー状態値を解読します。この例では簡潔にするため,imc_api_init
関数を除く,他のすべての関数からの状態を無視しています。
[例に戻る]
imc_asalloc
関数は,次のような特性を持つ Memory Channel のアドレス空間の領域を割り当てます。
key=123
-- この値で Memory Channel のアドレス空間の領域を識別します。この領域にアタッチする他のアプリケーションは同じキー値を使います。
size=8192
-- この領域のサイズは 8192 バイトです。
perm=IMC_URW
-- この領域のアクセス許可は,ユーザによる読み取り/書き込みが可能です。
id=glob_id
--
imc_asalloc
関数がこの変数に値を返します。この値は,割り当てられた領域を一意に識別します。プログラムでは,続いて Memory Channel の他の関数を呼び出す際に,この値を使います。
logical_rail=0
-- この領域は,Memory Channel の論理レール 0 を使って割り当てられます。
マスタ・プロセスでは,imc_asattach
関数を呼び出し,glob_id
識別子を指定して,送信用にこの領域をアタッチします。glob_id
識別子は,imc_asalloc
関数を呼び出して返された値です。imc_asattach
関数は,add_tx_ptr
を返します。これは,プロセスの仮想アドレス空間の領域のアドレスへのポインタです。値
IMC_SHARED
は,この領域が共用できることを示します。つまり,このホスト上の他のプロセスも,この領域にアタッチできます。
[例に戻る]
このプログラムは,グローバル・レコードのポインタ変数に,プロセスの仮想アドレス空間にある,仮想メモリの領域 (これは,Memory Channel の領域に対応している) へのポインタを代入し,このグローバル・レコードの
pid
フィールドに,そのプロセス ID を書き込みます。マスタ・プロセスは,この領域を送信用にアタッチしていることに注意してください。したがって,実行できるのは,フィールドへのデータの書き込みだけです。たとえば,次のようにしてこのフィールドを読み取ろうとすると,セグメント違反になります。
(pid_t)x = global_record->pid;
VALID
フラグを設定する前に,メモリ・バリア命令を使って,pid
フィールドを,Alpha CPU の書き込みバッファから強制的に書き出します。
[例に戻る]
スレーブ・プロセスが,glob_id
識別子を指定して
imc_asattach
関数を呼び出して,受信用にこの領域にアタッチします。
この識別子は,imc_asalloc
関数を呼び出した際に返された値です。imc_asattach
関数は
add_rx_ptr
を返します。これは,プロセスの仮想アドレス空間の領域のアドレスへのポインタです。マッピングした時点では,この領域をマッピングしたすべてのプロセスで,その領域の内容に一貫性がない可能性があります。そこで,マスタ・プロセスより先にスレーブ・プロセスを起動して,マスタ・プロセスによるすべての書き込みが,スレーブ・プロセスの仮想アドレス空間に反映されるようにします。
[例に戻る]
スレーブ・プロセスは,この領域にグローバル・レコードの構造体を当てはめ,valid フラグをポールします。このフラグは最初に volatile として定義されているため,コンパイラによる最適化に影響されず,フィールドの内容がレジスタに格納されます。これにより,このループ内の命令を実行するたびに,メモリから新しい値がロードされ,VALID
への遷移が正しく検出されます。
[例に戻る]
最後に,マスタ・プロセスとスレーブ・プロセスは,imc_asdetach
関数および
imc_asdealloc
関数を呼び出して,明示的にこの領域をデタッチし,割り当てを解除します。異常終了した場合には,プロセスが終了する際に,割り当てられた領域が自動的に解放されます。
[例に戻る]
10.6.2 項で説明したように,初期状態の一貫性の問題は,同じ領域への受信用のすべてのマッピングが完了した後に,データを再送信することで解決できます。または,割り当てるときに,その領域を一貫性のある領域として指定することで解決できます。ただし,プロセスが送信ポインタに書き込んでから,アップデートされたデータが,受信ポインタに対応する物理メモリに反映されるまで,数マイクロ秒かかる場合があります。この期間に,プロセスが受信ポインタを読み取ると,読み取られたデータは正しくありません。このことは,遅延にかかわる一貫性の問題として知られています。
遅延の問題は,通常の共用メモリ・システムでは発生しません。メモリとキャッシュの制御で,格納およびロード命令と,データ伝送との同期が確実にとれるようになっているためです。
例 10-2
は
/usr/examples/cluster/mc_ex2.c
にあります。この例では,グローバルなプロセス・カウンタの値を減らしていき,カウンタの値が 0 になるのを検証するプログラムの 2 つのバージョンを示します。最初のプログラムでは,System V の共用メモリとプロセス間通信を使用しています。2 番目のプログラムでは,Memory Channel API ライブラリを使用しています。
例 10-2: System V IPC および Memory Channel のコードの比較
/* * To compile : cc -o mc_ex2 -g mc_ex2.c -limc */ #if 0 /**************************************** ********* System V IPC example ******* ****************************************/ #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> main() { typedef struct { int proc_count; int remainder[2047]; } global_page; global_page *mypage; int shmid; shmid = shmget(123, 8192, IPC_CREAT | SHM_R | SHM_W); (caddr_t)mypage = shmat(shmid, 0, 0); /* attach the global region */ mypage->proc_count ++; /* increment process count */ /* body of program goes here */ . . . /* clean up */ mypage->proc_count --; /* decrement process count */ if (mypage->proc_count == 0 ) printf("The last process is exiting\n"); . . . } /**************************************** ******* Memory Channel example ******* ****************************************/ #include <sys/types.h> #include <sys/imc.h> main() { typedef struct { int proc_count; int remainder[2047]; } global_page; global_page *mypage_rx, *mypage_tx; [1] imc_asid_t glob_id; int logical_rail=0; int temp; imc_api_init(NULL); imc_asalloc(123, 8192, IMC_URW | IMC_GRW, 0, &glob_id, logical_rail); [2] imc_asattach(glob_id, IMC_TRANSMIT, IMC_SHARED, IMC_LOOPBACK, &(caddr_t)mypage_tx); [3] imc_asattach(glob_id, IMC_RECEIVE, IMC_SHARED, 0, &(caddr_t)mypage_rx); [4] /* increment process count */ mypage_tx->proc_count = mypage_rx->proc_count + 1; [5] /* body of program goes here */
.
.
.
/* clean up */ /* decrement process count */ temp = mypage_rx->proc_count - 1; [6] mypage_tx->proc_count = temp; /* wait for MEMORY CHANNEL update to occur */ while (mypage_rx->proc_count != temp) ; if (mypage_rx->proc_count == 0 ) printf("The last process is exiting\n"); }
このプロセスは,Memory Channel のグローバル・アドレス空間に書き込んだデータを読み取ることができなければなりません。そのため,送信用と受信用に 2 つのアドレスを宣言します。 [例に戻る]
imc_asalloc
関数は Memory Channel のアドレス空間の領域を割り当てます。この領域の特性は次のとおりです。
key=123
-- この値で Memory Channel のアドレス空間の領域を識別します。この領域にアタッチする他のアプリケーションは同じキー値を使います。
size=8192
-- この領域のサイズは 8192 バイトです。
perm=IMC_URW | IMC_GRW
-- この領域のアクセス許可は,ユーザおよびグループによる読み取り/書き込み可能として割り当てられます。
id=glob_id
--
imc_asalloc
関数がこの変数に値を返します。この値は,割り当てられた領域を一意に識別します。プログラムでは,続いて Memory Channel の他の関数を呼び出す際に,この値を使います。
logical_rail=0
-- この領域は,Memory Channel の論理レール 0 を使って割り当てられます。
ここで
imc_asattach
関数を呼び出して,mypage_tx
変数が指すアドレスに,送信用に領域をアタッチします。flag
パラメータの値として
IMC_LOOPBACK
を指定し,プロセスがこの領域に書き込みを行うたびに,受信メモリにそのデータがループバックされるようにします。
[例に戻る]
ここで
imc_asattach
関数を呼び出して,mypage_rx
変数が指すアドレスに,受信用に領域をアタッチします。
[例に戻る]
受信ポインタが指す値に 1 を加え,その結果を送信ポインタが指すカウンタに代入することで,グローバル・プロセス・カウンタをインクリメントします。送信ポインタに書き込みを行う際,プログラムでは,書き込み命令が完了するのを待ちません。 [例に戻る]
プログラムの本体が完了した後,このプログラムはプロセスのカウンタをデクリメントし,デクリメントした値がクラスタ内の他のホストに伝送されたかどうかをテストします。デクリメントしたカウントであること (過渡的な値ではなく) を検証するために,このカウンタをローカル変数
temp
に格納します。デクリメントした値を送信領域に書き込み,受信領域に到着する値が
temp
の値と一致するまで待ちます。一致したことで,プログラムは,デクリメントしたプロセス・カウンタが Memory Channel のアドレス空間に書き込まれたことが分かります。
[例に戻る]
この例では,ローカル変数を使うことで,プログラムが,受信側のメモリの値と送信された値を比較していることが保証されます。受信側メモリの値がアップデートされたことを確認する前にその値を使うと,誤ったデータを読み込んでしまう可能性があります。
10.6.6 エラー管理
共用メモリ・システムでは,メモリへの読み取りおよび書き込み処理で,エラーは発生しないと考えられています。Memory Channel システムでのエラーの発生頻度は 1 年に 3 回程度です。これは通常のネットワークおよび入出力サブシステムのエラー発生頻度に比べ,格段に低い数値です。
Memory Channel のハードウェアは,検出されたエラーを Memory Channel のソフトウェアに報告します。Memory Channel のハードウェアでは,アプリケーションでエラーに対処できるように,次の 2 つの保証をしています。
不正なデータをホスト・システムに書き込まない。
Memory Channel のハードウェアにデータが書き込まれた順番で,ホスト・システムにデータを送る。
これらが保証されていることで,信頼性の高い,効果的なメッセージ・システムの開発が容易になります。
Memory Channel API ライブラリには,アプリケーションでのエラー処理を作成する際に役立つ,次の関数があります。
imc_ckerrcnt_mr
--
imc_ckerrcnt_mr
関数は,Memory Channel のホスト上の指定した論理レールでエラーが発生していないか探します。これにより,送信側プロセスは,メッセージを送信するときに,エラーが発生しているかどうかを知ることができます。
imc_rderrcnt_mr
--
imc_rderrcnt_mr
関数は,指定した論理レールの,クラスタ単位のエラー・カウントを読み取り,呼び出し元のプログラムにその値を返します。これにより,受信側のプロセスは,受け取ったメッセージのエラー状態を学習することができます。
オペレーティング・システムでは,クラスタ内で発生したエラーの数をカウントしています。システムは,クラスタ内の Memory Channel のハードウェアでエラーが検出されるたびに,また,ホストがクラスタのメンバになったりクラスタから外れたりするたびに,この値をインクリメントします。
エラーの検出および処理には,時間が少し (0 ではない) かかります。クラスタ内の別のホストでちょうど今発生したエラーがあるかもしれないため,imc_rderrcnt_mr
関数から返されるカウントは必ずしも最新ではありません。ローカル・ホストでは,このカウントは常に最新のデータになっています。
imc_rderrcnt_mr
関数を使って,単純で,効率的なエラー検出を実現することができます。メッセージを送信する前に,エラー・カウントを読み取って,メッセージに,このエラー・カウントを添付して送信します。受信側のプロセスは,メッセージを受け取った際に,メッセージに添付されているエラー・カウントと,メッセージの到着後に取り出したローカル値を比較します。ローカル値は,最新のデータであることが保証されているので,この値が送信されてきた値と同じであれば,その間にエラーが発生していないことが分かります。例 10-3
で,この方法を実際に示します。この例は
/usr/examples/cluster/mc_ex3.c
にあります。
例 10-3: imc_rderrcnt_mr 関数を使ったエラー検出
/* * * To compile : cc -DTRANSMIT -o mc_ex3_tx -g mc_ex3.c -limc * cc -o mc_ex3_rx -g mc_ex3.c -limc * */ #ifdef TRANSMIT /***************************************** ********* Transmitting Process ********* ******************************************/ #include <c_asm.h> #define mb() asm("mb") #include <sys/imc.h> main() { typedef struct { volatile int msg_arrived; int send_count; int remainder[2046]; } global_page; global_page *mypage_rx, *mypage_tx; imc_asid_t glob_id; int i; volatile int err_count; imc_api_init(NULL); imc_asalloc (1234, 8192, IMC_URW, 0, &glob_id,0); imc_asattach (glob_id, IMC_TRANSMIT, IMC_SHARED, IMC_LOOPBACK, &(caddr_t)mypage_tx); imc_asattach (glob_id, IMC_RECEIVE, IMC_SHARED, 0, &(caddr_t)mypage_rx); /* save the error count */ while ( (err_count = imc_rderrcnt_mr(0) ) < 0 ) ; mypage_tx->send_count = err_count; /* store message data */ for (i = 0; i < 2046; i++) mypage_tx->remainder[i] = i; /* now mark as valid */ mb(); do { mypage_tx->msg_arrived = 1; } while (mypage_rx->msg_arrived != 1); /* ensure no error on valid flag */ } #else /***************************************** *********** Receiving Process ********** ******************************************/ #include <sys/imc.h> main() { typedef struct { volatile int msg_arrived; int send_count; int remainder[2046]; } global_page; global_page *mypage_rx, *mypage_tx; imc_asid_t glob_id; int i; volatile int err_count; imc_api_init(NULL); imc_asalloc (1234, 8192, IMC_URW, 0, &glob_id,0); imc_asattach (glob_id, IMC_RECEIVE, IMC_SHARED, 0, &(caddr_t)mypage_rx); /* wait for message arrival */ while ( mypage_rx->msg_arrived == 0 ) ; /* get this systems error count */ while ( (err_count = imc_rderrcnt_mr(0) ) < 0 ) ; if (err_count == mypage_rx->send_count) { /* no error, process the body */ } else { /* do error processing */ } } #endif
例 10-3
に示すとおり,imc_rderrcnt_mr
関数をメッセージの受信側で使うと,エラーを確実に検出することができます。ただし,送信側で使った場合は,エラーの検出は保証されません。これは,受信側ホストで発生したエラーが送信側ホストに通知される前に,送信側プロセスがエラー・カウントを読み取る可能性が少しある (0 ではない) ためです。例 10-3
では,送信側ホストにエラーを通知するためには,高レベルのプロトコルを使用しなければなりません。
imc_ckerrcnt_mr
関数を使うと,指定した論理レールのエラーを確実に検出することができます。この関数は,ユーザが用意したローカルのエラー・カウントと,論理レールの番号をパラメータとして受け取り,次のような場合にエラーを返します。
指定した論理レール上で,未処理のエラーが検出された場合。
エラー処理が進行中の場合。
エラー・カウントが,パラメータで指定されたものより大きい場合。
関数が正常に戻ってくれば,ローカルのエラー・カウントが格納されてから,imc_ckerrcnt_mr()
関数が呼び出されるまでの間に,エラーが検出されなかったことになります。
imc_ckerrcnt_mr
関数は,指定した論理レールの Memory Channel アダプタのハードウェア・エラー状態を読み取ります。これは,数マイクロ秒かかる,ハードウェア操作です。そのため,メモリの内容を読み取るだけの
imc_rderrcnt_mr
関数に比べ,imc_ckerrcnt_mr
関数の方が,実行に時間がかかります。
例 10-4
は,例 10-3
で示した送信手順の改訂版です。例 10-4
では,伝送側プロセスが,エラーの検出を行っています。この例は
/usr/examples/cluster/mc_ex4.c
にあります。
例 10-4: imc_ckerrcnt_mr 関数を使ったエラー検出
/* * * To compile: cc -o mc_ex4 -g mc_ex4.c -limc * */ #include <c_asm.h> #define mb() asm("mb") #include <sys/imc.h> main() { typedef struct { volatile int msg_arrived; int send_count; int remainder[2046]; } global_page; global_page *mypage_rx, *mypage_tx; imc_asid_t glob_id; int i, status; volatile int err_count; imc_api_init(NULL); imc_asalloc (1234, 8192, IMC_URW, 0, &glob_id,0); imc_asattach (glob_id, IMC_TRANSMIT, IMC_SHARED, IMC_LOOPBACK, &(caddr_t)mypage_tx); imc_asattach (glob_id, IMC_RECEIVE, IMC_SHARED, 0, &(caddr_t)mypage_rx); /* save the error count */ while ( (err_count = imc_rderrcnt_mr(0) ) < 0 ) ; do { mypage_tx->send_count = err_count; /* store message data */ for (i = 0; i < 2046; i++) mypage_tx->remainder[i] = i; /* now mark as valid */ mb(); mypage_tx->msg_arrived = 1; /* if error occurs, retransmit */ } while ( (status = imc_ckerrcnt_mr(&err_count,0)) != IMC_SUCCESS); }
Memory Channel システムでは,プロセスは Memory Channel のアドレス空間の領域を読み書きして,通信します。前の節では,領域に自由に読み書きするサンプル・プログラムを示しました。しかし実際は,ロック・メカニズムを使って,領域や他のクラスタ単位のリソースへのアクセスを制御する必要がある場合があります。Memory Channel API ライブラリには,ロックのための一連の関数が用意されています。これらの関数を使えば,アプリケーションでリソースへのアクセス制御を実現することができます。
Memory Channel API ライブラリは,Memory Channel のグローバル・アドレス空間にマッピングされたページを使って,ロックを実現しています。効率を考え,ロックは個々にではなく,セットで割り当てられます。imc_lkalloc
関数を使うと,ロック・セットを割り当てることができます。たとえば,20 個のロックを使用したい場合は,20 個のロックを 1 セット作成する方が,4 個のロックを 5 セット作成するよりも効率的です。
分散型アプリケーションでの最初の調整を簡単に行いたい場合は,imc_lkalloc
関数を使うことで,プロセスはアトミックに (つまり,1 回の操作で) ロック・セットを割り当て,そのセットの最初のロックを取得することができます。この機能を使用すると,プロセスはそれ自身がそのロック・セットを割り当てた最初のプロセスかどうかが分かります。最初のプロセスであった場合,そのプロセスはロックへのアクセスが保証され,リソースを確実に初期化することができます。
ロック・セットを割り当てて,最初のロックをアトミックに取得する代わりに,プロセスはまず,imc_lkalloc
関数を呼び出し,その後で,imc_lkacquire
関数を呼び出すこともできます。この場合,1 つ目の関数を呼び出して 2 つ目の関数を呼び出すまでの間に,別のプロセスがそのロックを取得してしまうという恐れがあります。この場合,最初のプロセスはロックへのアクセスが保証されません。
例 10-5
では,Memory Channel のアドレス空間の領域をロックする最初のプロセスがその領域を初期化し,次にこの領域にアクセスするプロセスが,プロセス・カウンタをアップデートする単純なプログラムを示します。この例は
/usr/examples/cluster/mc_ex5.c
にあります。
例 10-5: Memory Channel の領域のロック
/* * * To compile: cc -o mc_ex5 -g mc_ex5.c -limc * */ #include <sys/types.h> #include <sys/imc.h> main ( ) { imc_asid_t glob_id; imc_lkid_t lock_id; int locks = 4; int status; typedef struct { int proc_count; int pattern[2047]; } clust_rec; clust_rec *global_record_tx, *global_record_rx; [1] caddr_t add_rx_ptr = 0, add_tx_ptr = 0; int j; status = imc_api_init(NULL); imc_asalloc(123, 8192, IMC_URW, 0, &glob_id, 0); imc_asattach(glob_id, IMC_TRANSMIT, IMC_SHARED, IMC_LOOPBACK, &add_tx_ptr); imc_asattach(glob_id, IMC_RECEIVE, IMC_SHARED, 0, &add_rx_ptr); global_record_tx = (clust_rec*) add_tx_ptr; [2] global_record_rx = (clust_rec*) add_rx_ptr; status = imc_lkalloc(456, &locks, IMC_LKU, IMC_CREATOR, &lock_id); [3] if (status == IMC_SUCCESS) { /* This is the first process. Initialize the global region */ global_record_tx->proc_count = 0; [4] for (j = 0; j < 2047; j++) global_record_tx->pattern[j] = j; /* release the lock */ imc_lkrelease(lock_id, 0); [5] } /* This is a secondary process */ else if (status == IMC_EXISTS) { imc_lkalloc(456, &locks, IMC_LKU, 0, &lock_id); [6] imc_lkacquire(lock_id, 0, 0, IMC_LOCKWAIT); [7] /* wait for access to region */ global_record_tx->proc_count = global_record_rx->proc_count+1; [8] /* release the lock */ imc_lkrelease(lock_id, 0); } /* body of program goes here */ /* clean up */ imc_lkdealloc(lock_id); [9] imc_asdetach(glob_id); imc_asdealloc(glob_id); }
このプロセスは,Memory Channel のグローバル・アドレス空間に自身で書き込んだデータを読み取るために,領域を送信用と受信用にマップします。この手順についての詳細は,例 10-2 を参照してください。 [例に戻る]
プログラムは,送信ポインタと受信ポインタに,グローバル・レコードの構造体を代入します。 [例に戻る]
このプロセスは,4 つのロックからなり,key
の値が
456
のロック・セットを作成しようとします。imc_lkalloc
関数の呼び出しでは,IMC_CREATOR
フラグも指定します。こうすると,ロック・セットがまだ割り当てられていなかった場合は,この関数が自動的に 0 番のロックを取得します。このロック・セットが既に存在していた場合,imc_lkalloc
関数はロック・セットの割り当てに失敗し,値
IMC_EXISTS
を返します。
[例に戻る]
ロック・セットを作成してロック番号 0 を取得したプロセスが,グローバル領域を初期化します。 [例に戻る]
このプロセスは,この領域の初期化が終了すると,imc_lkrelease
関数を呼び出して,ロックを解放します。
[例に戻る]
この領域が初期化された後に実行される 2 番目のプロセスは,初めの
imc_lkalloc
関数の呼び出しに失敗し,今度は
IMC_CREATOR
フラグを指定せずに,この関数を再度呼び出します。key
パラメータの値が同じ
(456)
なので,この関数呼び出しでは,同じロック・セットが割り当てられます。
[例に戻る]
2 番目のプロセスは
imc_lkacquire
関数を呼び出して,ロック・セットからロック 0 を取得します。
[例に戻る]
2 番目のプロセスは,プロセス・カウンタをアップデートし,その値を送信領域に書き込みます。 [例に戻る]
プログラムの最後で,このプロセスはすべての Memory Channel リソースを解放します。 [例に戻る]
あるプロセスがロックを取得すると,同じクラスタ上の他のプロセスは,そのロックを取得することができません。
ロックが解放されるのを待つと,プログラムがループして待つことになるため,性能に大きな影響を及ぼします。そのため,システム全体の性能を考え,アプリケーションでは,必要なロックだけを取得し,できるだけ早く解放するようにしてください。
10.8 クラスタ・シグナル
Memory Channel API ライブラリには,プロセスが,クラスタ内のリモート・ホスト上で動作している特定のプロセスにシグナルを送ることができるようにする,imc_kill
関数があります。この関数は UNIX の
kill
(2)kill
関数をクラスタで使用すると,そのプロセスが他のクラスタ・メンバで動作していても,プロセス・グループ番号が PID の絶対値と同じすべてのプロセスへシグナルが送られます。PID はクラスタ全体で一意であることが保証されています。
ただし,imc_kill
関数は
kill
関数と違って,シグナルをクラスタの他のメンバに送れないだけでなく,複数のプロセスに送ることもできません。
10.9 クラスタ情報
以降の各項では,Memory Channel API 関数を使ってクラスタ情報にアクセスする方法と,コマンド行から状態情報にアクセスする方法を説明します。
10.9.1 Memory Channel API の関数を使って Memory Channel API クラスタ情報にアクセスする方法
Memory Channel API ライブラリには,imc_getclusterinfo
関数があります。この関数を使うと,プロセスは Memory Channel API クラスタ内のホストに関する情報を取得することができます。この関数は,次の情報を返します。
クラスタ内のホストの数,およびそれぞれのホスト名。
クラスタ内の論理レールの数。
Memory Channel のアクティブな論理レールを示すビットマスク。ビットが 1 のとき,アクティブな論理レールを示す。
Memory Channel API ライブラリが初期化されていないホストでは,この関数を使っても,そのホストの情報は返されません。
Memory Channel API ライブラリには,imc_wait_cluster_event
関数があります。この関数は,指定したクラスタのイベントが発生するまで,呼び出し元のスレッドをブロックします。次の Memory Channel API クラスタのイベントが有効です。
ホストがクラスタのメンバになる,またはメンバでなくなる。
クラスタの論理レール構成が変化する。
imc_wait_cluster_event
関数は,監視対象の Memory Channel API クラスタ構成の項目の現在の状況を調べ,新しい Memory Channel API クラスタ構成を返します。
例 10-6
では,imc_getclusterinfo
関数を
imc_wait_cluster_event
関数と併用して,Memory Channel API のクラスタ・メンバの名前と,Memory Channel のアクティブな論理レールのビットマスクを要求し,どちらかが変化したことを示すイベントを待つ方法を示します。この例は
/usr/examples/cluster/mc_ex6.c
にあります。
例 10-6: Memory Channel API クラスタ情報を要求し,Memory Channel API クラスタ・イベントを待つ方法
/* * * To compile: cc -o mc_ex6 -g mc_ex6.c -limc * */ #include <sys/imc.h> main ( ) { imc_railinfo mask; imc_hostinfo hostinfo; int status; imc_infoType items[3]; imc_eventType events[3]; items[0] = IMC_GET_ACTIVERAILS; items[1] = IMC_GET_HOSTS; items[2] = 0; events[0] = IMC_CC_EVENT_RAIL; events[1] = IMC_CC_EVENT_HOST; events[2] = 0; imc_api_init(NULL); status = imc_getclusterinfo(items,2,mask,sizeof(imc_railinfo), &hostinfo,sizeof(imc_hostinfo)); if (status != IMC_SUCCESS) imc_perror("imc_getclusterinfo:",status); status = imc_wait_cluster_event(events, 2, 0, mask, sizeof(imc_railinfo), &hostinfo, sizeof(imc_hostinfo)); if ((status != IMC_HOST_CHANGE) && (status != IMC_RAIL_CHANGE)) imc_perror("imc_wait_cluster_event didn't complete:",status); } /*main*/
10.9.2 コマンド行から Memory Channel の状態情報にアクセスする方法
Memory Channel API ライブラリでは,Memory Channel の状態を報告する
imcs
コマンドを用意しています。imcs
コマンドは,現在アクティブな Memory Channel のファシリティに関する情報を,標準出力に表示します。出力結果には,領域やロック・セットがリストされ,次のような情報が示されます。
領域やロック・セットを作成した,サブシステムのタイプ (IMC または PVM)
Memory Channel の領域の識別子
Memory Channel の領域やロック・セットを示す,アプリケーション固有のキー
領域のサイズ (バイト単位)
領域やロック・セットのアクセス・モード
領域やロック・セットの所有者のユーザ名
領域やロック・セットの所有者のグループ
その領域で使用されている Memory Channel の論理レール
その領域の一貫性を示すフラグ
そのロック・セットで使用できるロックの数
割り当てられた領域の総数
Memory Channel API のオーバヘッド
Memory Channel のレールの使用状況
Memory Channel API ライブラリをベースにアプリケーションを開発する場合,使用できるモデルが 2 つあります。
共用メモリ・モデル
メッセージ渡しモデル
最初は,共用メモリ・モデルを使ったアプローチの方が,Memory Channel の機能には適しているように見えるかもしれません。しかし,このモデルを使用する開発者は,この章で説明した,遅延の問題,一貫性の問題,およびエラー検出の問題に対処しなければなりません。場合によっては,これらの問題をアプリケーションから隠す,単純なメッセージ渡しのライブラリを開発した方が適していることがあります。このようなライブラリでのデータ伝送関数は,ユーザ空間内で完全に実現することができます。そのため,これらの関数は,共用メモリ・モデルを基にして実現されたものと同じぐらい効率的に動作します。
10.11 よくある質問 (FAQ)
この節では,Memory Channel API を使って TruCluster システムのプログラムを開発しているプログラマから寄せられた質問に対する回答を示します。
10.11.1 IMC_NOMAPPER リターン・コード
質問:
imc_asattach
関数を使って,一貫性のある領域にアタッチしようとしましたが,値
IMC_NOMAPPER
が返されました。これはどういう意味ですか。
回答: このリターン値は,ご使用の Memory Channel API クラスタ内のシステムに
imc_mapper
プロセスが存在しないことを示しています。
imc_mapper
プロセスは,次の場合に自動的に起動されます。
システムの初期化時に,構成変数
IMC_AUTO_INIT
の値が 1 の場合。IMC_AUTO_INIT
変数についての詳細は,10.2 節を参照。
imc_init
コマンドを初めて実行した場合。
この問題を解決するには,imc_mapper
プロセスが消えているシステムをリブートしてください。
このエラーは,システムをシャットダウンして
init
レベル 3 からシングルユーザ・モードにし,完全なリブートの手順を踏まないで,マルチユーザ・モードに戻した場合に発生する可能性があります。TruCluster Server ソフトウェアが動作しているシステムをリブートする場合は,そのシステムの完全なリブート手順を踏まなければなりません。
10.11.2 効率的なデータ・コピー
質問: Memory Channel の帯域幅を最大限に生かすためには,どのようにデータを Memory Channel の送信領域にコピーするとよいですか。
回答: Memory Channel API の
imc_bcopy
関数を使用すると,位置合わせされたデータと位置合わせされていないデータのどちらでも効率的に Memory Channel にコピーすることができます。imc_bcopy
関数は,AlphaServer CPU のバッファリング機能を最大限に利用するように最適化されています。
imc_bcopy
関数を使って,通常のメモリの 2 つのバッファ間で,データを効率的にコピーすることもできます。
10.11.3 Memory Channel の帯域幅の可用性
質問: Memory Channel の一貫性のある領域を使った場合,Memory Channel の帯域幅を最大限に利用できますか。
回答: できません。一貫性のある領域では,局所的な一貫性を維持するために,ループバック機能が使われます。したがって,それぞれのデータ書き込みサイクルに対応して,データをループバックするサイクルがあるため,利用可能な帯域幅は半分になります。ループバック機能についての詳細は,10.6.1.3 項を参照してください。
10.11.4 Memory Channel API クラスタ構成の変更
質問: プログラムでは,Memory Channel API クラスタ構成に変更があったかどうかを,どのようにして確認することができますか。
回答: 新しい
imc_wait_cluster_event
関数を使うと,ホストが Memory Channel API クラスタのメンバになったり外れたりする状況や,アクティブな論理レールの状態の変化を監視することができます。プログラムでは,別のスレッドで
imc_wait_cluster_event
関数を呼び出すようにすることができます。この関数では,状態が変化するまで,呼び出し元がブロックされるためです。
10.11.5 バス・エラー・メッセージ
質問: プログラム内で,アタッチした送信領域に値を設定しようとすると,次のようなメッセージが出力されてクラッシュします。
Bus error (core dumped)
なぜこのようなエラーが発生するのですか。
回答: その値のデータ型が 32 ビットよりも小さい可能性があります。C 言語では,int
は 32 ビットのデータ項目,short
は 16 ビットのデータ項目です。HP Alpha プロセッサは,他の RISC プロセッサと同様に,データの読み取り/書き込みを 64 ビット単位,または 32 ビット単位で行います。32 ビットより小さい値をデータ項目に代入した場合,コンパイラは,32 ビット単位のデータをロードして,変更すべき部分のバイトを変更し,その後,32 ビット単位全体を格納するコードを生成します。このようなデータ項目が,送信用にアタッチされた Memory Channel の領域にある場合,代入を実行すると,アタッチされた領域内で読み取り操作が発生します。送信領域は書き込み専用なので,バス・エラーが発生します。
この問題は,すべてのアクセスが 32 ビットのデータ項目に対して行われるようにすることで回避できます。詳細は,10.6.3 項を参照してください。