10    Memory Channel API ライブラリ

Memory Channel アプリケーション・プログラミング・インタフェース (API) を使うと,自動エラー処理,ロック,および UNIX スタイルの保護を使用して,Memory Channel API クラスタ・メンバ間で効率のよいメモリ共用が実現できます。この章では,Memory Channel API ライブラリをベースにしたアプリケーションの開発に役立つ情報を説明します。また,Memory Channel のアドレス空間と,一般的な共用メモリとの違いを説明し,トランスポート層の機構として,Memory Channel を使うプログラミングと,共用メモリを使うプログラミングでは,どのように異なるかを説明します。

注意

Memory Channel API ライブラリは,Memory Channel ハードウェアで構成されたクラスタでだけサポートされます。Memory Channel ハードウェアが実装されていないクラスタでは,アプリケーションはクラスタ単位でのキル・メカニズム ( kill(1)kill(2) を参照) を使用する場合があります。このメカニズムは Memory Channel API の 1 つの機能の代わりとして使うことができます。

この章では,プログラムで Memory Channel API ライブラリの関数を使用する方法についても,例を使って説明します。このプログラムのソース・ファイルは,/usr/examples/cluster/ ディレクトリにあります。各ファイルには,コンパイルの指示が記載されています。

この章では,次のトピックについて説明します。

10.1    Memory Channel マルチレール・モデル

Memory Channel マルチレール・モデルには,物理レールと論理レールの概念があります。物理レールとは,ケーブルおよび Memory Channel アダプタが接続された Memory Channel ハブ (物理または仮想) と,各ノードのアダプタ用の Memory Channel ドライバのことをいいます。論理レールは,1 つまたは 2 つの物理レールで構成されます。

1 つのクラスタは,1 つ以上,最大 4 つまでの論理レールを持つことができます。論理レールは,次のスタイルで構成することができます。

10.1.1    シングルレール・スタイル

クラスタがシングルレール・スタイルで構成されている場合,物理レールと論理レールは 1 対 1 に対応します。この構成では,フェイルオーバには対応できません。つまり,物理レールに障害が起きた場合,論理レールにも障害が発生することになります。

シングルレール構成を使う利点は,アプリケーションがすべての論理レールのアドレス空間の集合体にアクセスでき,集合体の帯域幅を利用して最大限の性能を実現できることです。

図 10-1 は,3 つの論理レールからなるシングルレールの Memory Channel の構成を示します。各論理レールには,それぞれ物理レールが対応しています。

図 10-1:  シングルレール Memory Channel の構成

10.1.2    フェイルオーバ・ペア・スタイル

クラスタがフェイルオーバ・ペア・スタイルで構成されている場合,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 パラメータには,次の値のどちらかが設定されます。

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 パラメータを変更した場合は,システムをリブートしなければなりません。

注意

大半の運用形態では,省略時の固定メモリの容量で十分に対応できます。この限界値を変更する場合は十分に注意するようにしてください。

10.5    トラブルシューティング

以降の各項では,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 ログ・ファイルに書き込まれます。次に示す一連のエラー・メッセージとその解決方法を参考にして,エラーを取り除いてください。

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 がすべてのノードで同じでない場合に発生します。

この問題を解決するには,次の手順を実行してください。

  1. システムを停止します。

  2. /genvmunix をブートします。

  3. 10.1.3 項の説明に従い,/etc/sysconfigtab ファイルを修正します。

  4. Memory Channel API クラスタ・サポート (/vmunix) を使ってカーネルをリブートします。

10.5.4    IMC_MCFULL リターン・コード

ある操作を実行するのに Memory Channel のアドレス空間が不足していると,IMC_MCFULL 状態が返されます。

Memory Channel API ライブラリで利用できる Memory Channel のアドレス空間の総容量は,10.5.2 項で説明しているように,imc_init コマンドの maxalloc パラメータで指定します。

rcmgr(8) コマンド,または Memory Channel API ライブラリの初期化コマンド 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) コマンド,または Memory Channel API ライブラリの初期化コマンド 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 インターコネクトを使ってメモリを共用する方式は,接続を確立すれば,仮想アドレス空間にアクセスするだけで,2 つの異なるプロセスがデータを共用できるという点では,通常の共用メモリと似ています。ただし,これらのメモリ共用方式には,次のような考慮しなければならない違いが 2 つあります。

10.6.1    Memory Channel のアドレス空間へのアタッチ

以降の各項では,プロセスに Memory Channel のアドレス空間をアタッチさせる方法を説明します。プロセスに Memory Channel のアドレス空間をアタッチさせる方法は,次のとおりです。

この節では,初期状態の一貫性,Memory Channel 領域の読み取り/書き込み,遅延と一貫性,およびエラー管理についても説明し,コーディング例をいくつか示します。

10.6.1.1    ブロードキャスト・アタッチ

あるプロセスが送信用にマップした領域を,別のプロセスが受信用にマップした場合,送信側プロセスがその領域に書き込んだデータは Memory Channel を通して,もう一方のプロセスの受信メモリに伝送されます。図 10-3 は,ホストが 3 つある Memory Channel システムで,アドレス空間がどのように対応するかを示します。

図 10-3:  ブロードキャスト・アドレス空間のマッピング

上記の図 10-3 のようにアドレス空間をマッピングすると,次のようになります。

  1. プロセス A は,Memory Channel のアドレス空間のある領域を割り当てます。次に,このプロセス A は,imc_asattach 関数を使ってこの領域を送信用にアタッチして,この領域を自分の仮想アドレス空間にマップします。

  2. プロセス B と C は,どちらもプロセス A と同じ Memory Channel のアドレス空間の領域を割り当てます。ただし,プロセス A とは異なり,プロセス B と C は,データを受信するためにこの領域にアタッチします。

  3. データがプロセス A の仮想アドレス空間に書き込まれると,そのデータは Memory Channel 上で伝送されます。

  4. プロセス A からのデータが Memory Channel に送られると,このデータは,プロセス B および C の,データ受信用に割り当てられた仮想アドレス空間に対応する物理メモリに書き込まれます。

10.6.1.2    ポイント・ツー・ポイント・アタッチ

割り当てた Memory Channel のアドレス空間の領域は,別のノード上の特定のプロセスの仮想アドレス空間に,送信用としてポイント・ツー・ポイント・モードでアタッチすることができます。これは,パラメータでホストを指定し,imc_asattach_ptp 関数を呼び出すことで実行できます。これにより,この領域に書き込んだデータは,クラスタ内のすべてのホストに送られるのではなく,パラメータで指定したホストにのみ送られるようになります。

imc_asattach_ptp 関数を使ってアタッチする領域は,常に送信モードでアタッチし,書き込みのみが可能です。図 10-4 は,ホストが 2 つある Memory Channel システムで,ポイント・ツー・ポイントのアドレス空間がどのように対応するかを示します。

図 10-4:  ポイント・ツー・ポイント・アドレス空間のマッピング

上記の図 10-4 のようにアドレス空間をマッピングすると,次のようになります。

  1. プロセス 1 は,Memory Channel のアドレス空間の領域を割り当てます。次に,imc_asattach_ptp 関数を使って,この領域をホスト B とのポイント・ツー・ポイント用としてアタッチすることで,この領域を自分の仮想アドレス空間にマップします。

  2. プロセス 2 は,この領域を割り当て,imc_asattach 関数を使って,受信用としてこの領域にアタッチします。

  3. データがプロセス 1 の仮想アドレス空間に書き込まれると,このデータは Memory Channel 上で伝送されます。

  4. プロセス 1 からのデータが Memory Channel に送られると,このデータは,ホスト B 上のプロセス 2 の仮想アドレス空間に対応する物理メモリに書き込まれます。

10.6.1.3    ループバック・アタッチ

Memory Channel の領域は,1 つのホスト上のプロセスから送信と受信の両方でアタッチすることができます。あるホストが書き込んだデータは,その領域を受信用にアタッチしている他のホストに書き込まれます。しかし,省略時の設定では,あるホストが書き込んだデータは,そのホストの受信用メモリには書き込まれないようになっています。データは,他のホストにのみ書き込まれます。書き込んだデータを,書き込んだホスト自身で参照できるようにするには,送信用にアタッチする際に,imc_asattach() 関数に IMC_LOOPBACK フラグを指定しなければなりません。

領域のループバック属性はホスト単位でセットアップし,そのホストでの最初の送信用アタッチで渡す flag パラメータの値で判断されます。

flag パラメータに値 IMC_LOOPBACK を指定すると,書き込みのたびに 2 つの Memory Channel トランザクションが発生します。1 つはデータの書き込み用で,もう 1 つはデータのループバック用です。

ポイント・ツー・ポイント・アタッチ方式では,性質上,ループバックによる書き込みは許されません。

図 10-5 では,Memory Channel のアドレス空間の領域を,ループバック付きの送信と受信の両方でアタッチする場合の構成を示します。

図 10-5:  ループバック・アドレス空間のマッピング

10.6.2    初期状態の一貫性

Memory Channel の領域に受信用にアタッチした場合,初期状態は不定です。このような状況は,Memory Channel のある領域を送信用にマップしているプロセスが,他のプロセスがその領域を受信用にマップする前に,その領域の内容をアップデートした場合に発生します。これは,初期状態の一貫性の問題と呼ばれます。この問題に対処する方法は 2 つあります。

10.6.3    Memory Channel 領域の読み取りと書き込み

Memory Channel のアドレス空間の領域をアタッチするプロセスは,送信ポインタにのみ書き込むことができ,受信ポインタからのみ読み取ることができます。送信ポインタから読み取ろうとすると,セグメント違反になります。

Memory Channel の送信ポインタからの明らかな読み取り操作以外に,コンパイラが読み取り,変更,書き込みのサイクルの命令を生成するような操作でも,セグメント違反が発生します。たとえば,次のような操作です。

10.6.4    アドレス空間の例

例 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]
}
 

  1. valid フラグを volatile として定義することで,コンパイラの最適化によって,アップデートした PID の値がメモリから読み取れなくなるのを防いでいます。 [例に戻る]

  2. コマンド行の最初の引数で,プロセスがマスタ・プロセス (引数は 1) か,スレーブ・プロセス (引数は 0) かを指定します。 [例に戻る]

  3. imc_api_init 関数は,Memory Channel API ライブラリを初期化します。この関数は,Memory Channel API ライブラリの他の関数を呼び出す前に呼び出さなければなりません。 [例に戻る]

  4. すべての Memory Channel API ライブラリの関数は,正常に終了した場合,リターン状態として 0 を返します。imc_perror 関数は,エラー状態値を解読します。この例では簡潔にするため,imc_api_init 関数を除く,他のすべての関数からの状態を無視しています。 [例に戻る]

  5. imc_asalloc 関数は,次のような特性を持つ Memory Channel のアドレス空間の領域を割り当てます。

    [例に戻る]

  6. マスタ・プロセスでは,imc_asattach 関数を呼び出し,glob_id 識別子を指定して,送信用にこの領域をアタッチします。glob_id 識別子は,imc_asalloc 関数を呼び出して返された値です。imc_asattach 関数は,add_tx_ptr を返します。これは,プロセスの仮想アドレス空間の領域のアドレスへのポインタです。値 IMC_SHARED は,この領域が共用できることを示します。つまり,このホスト上の他のプロセスも,この領域にアタッチできます。 [例に戻る]

  7. このプログラムは,グローバル・レコードのポインタ変数に,プロセスの仮想アドレス空間にある,仮想メモリの領域 (これは,Memory Channel の領域に対応している) へのポインタを代入し,このグローバル・レコードの pid フィールドに,そのプロセス ID を書き込みます。マスタ・プロセスは,この領域を送信用にアタッチしていることに注意してください。したがって,実行できるのは,フィールドへのデータの書き込みだけです。たとえば,次のようにしてこのフィールドを読み取ろうとすると,セグメント違反になります。

    (pid_t)x = global_record->pid;
     
    

    [例に戻る]

  8. VALID フラグを設定する前に,メモリ・バリア命令を使って,pid フィールドを,Alpha CPU の書き込みバッファから強制的に書き出します。 [例に戻る]

  9. スレーブ・プロセスが,glob_id 識別子を指定して imc_asattach 関数を呼び出して,受信用にこの領域にアタッチします。 この識別子は,imc_asalloc 関数を呼び出した際に返された値です。imc_asattach 関数は add_rx_ptr を返します。これは,プロセスの仮想アドレス空間の領域のアドレスへのポインタです。マッピングした時点では,この領域をマッピングしたすべてのプロセスで,その領域の内容に一貫性がない可能性があります。そこで,マスタ・プロセスより先にスレーブ・プロセスを起動して,マスタ・プロセスによるすべての書き込みが,スレーブ・プロセスの仮想アドレス空間に反映されるようにします。 [例に戻る]

  10. スレーブ・プロセスは,この領域にグローバル・レコードの構造体を当てはめ,valid フラグをポールします。このフラグは最初に volatile として定義されているため,コンパイラによる最適化に影響されず,フィールドの内容がレジスタに格納されます。これにより,このループ内の命令を実行するたびに,メモリから新しい値がロードされ,VALID への遷移が正しく検出されます。 [例に戻る]

  11. 最後に,マスタ・プロセスとスレーブ・プロセスは,imc_asdetach 関数および imc_asdealloc 関数を呼び出して,明示的にこの領域をデタッチし,割り当てを解除します。異常終了した場合には,プロセスが終了する際に,割り当てられた領域が自動的に解放されます。 [例に戻る]

10.6.5    遅延と一貫性

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"); }  

  1. このプロセスは,Memory Channel のグローバル・アドレス空間に書き込んだデータを読み取ることができなければなりません。そのため,送信用と受信用に 2 つのアドレスを宣言します。 [例に戻る]

  2. imc_asalloc 関数は Memory Channel のアドレス空間の領域を割り当てます。この領域の特性は次のとおりです。

    [例に戻る]

  3. ここで imc_asattach 関数を呼び出して,mypage_tx 変数が指すアドレスに,送信用に領域をアタッチします。flag パラメータの値として IMC_LOOPBACK を指定し,プロセスがこの領域に書き込みを行うたびに,受信メモリにそのデータがループバックされるようにします。 [例に戻る]

  4. ここで imc_asattach 関数を呼び出して,mypage_rx 変数が指すアドレスに,受信用に領域をアタッチします。 [例に戻る]

  5. 受信ポインタが指す値に 1 を加え,その結果を送信ポインタが指すカウンタに代入することで,グローバル・プロセス・カウンタをインクリメントします。送信ポインタに書き込みを行う際,プログラムでは,書き込み命令が完了するのを待ちません。 [例に戻る]

  6. プログラムの本体が完了した後,このプログラムはプロセスのカウンタをデクリメントし,デクリメントした値がクラスタ内の他のホストに伝送されたかどうかをテストします。デクリメントしたカウントであること (過渡的な値ではなく) を検証するために,このカウンタをローカル変数 temp に格納します。デクリメントした値を送信領域に書き込み,受信領域に到着する値が temp の値と一致するまで待ちます。一致したことで,プログラムは,デクリメントしたプロセス・カウンタが Memory Channel のアドレス空間に書き込まれたことが分かります。 [例に戻る]

この例では,ローカル変数を使うことで,プログラムが,受信側のメモリの値と送信された値を比較していることが保証されます。受信側メモリの値がアップデートされたことを確認する前にその値を使うと,誤ったデータを読み込んでしまう可能性があります。

10.6.6    エラー管理

共用メモリ・システムでは,メモリへの読み取りおよび書き込み処理で,エラーは発生しないと考えられています。Memory Channel システムでのエラーの発生頻度は 1 年に 3 回程度です。これは通常のネットワークおよび入出力サブシステムのエラー発生頻度に比べ,格段に低い数値です。

Memory Channel のハードウェアは,検出されたエラーを Memory Channel のソフトウェアに報告します。Memory Channel のハードウェアでは,アプリケーションでエラーに対処できるように,次の 2 つの保証をしています。

これらが保証されていることで,信頼性の高い,効果的なメッセージ・システムの開発が容易になります。

Memory Channel API ライブラリには,アプリケーションでのエラー処理を作成する際に役立つ,次の関数があります。

オペレーティング・システムでは,クラスタ内で発生したエラーの数をカウントしています。システムは,クラスタ内の 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);
}
 

10.7    クラスタ単位のロック

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);
}
 

  1. このプロセスは,Memory Channel のグローバル・アドレス空間に自身で書き込んだデータを読み取るために,領域を送信用と受信用にマップします。この手順についての詳細は,例 10-2 を参照してください。 [例に戻る]

  2. プログラムは,送信ポインタと受信ポインタに,グローバル・レコードの構造体を代入します。 [例に戻る]

  3. このプロセスは,4 つのロックからなり,key の値が 456 のロック・セットを作成しようとします。imc_lkalloc 関数の呼び出しでは,IMC_CREATOR フラグも指定します。こうすると,ロック・セットがまだ割り当てられていなかった場合は,この関数が自動的に 0 番のロックを取得します。このロック・セットが既に存在していた場合,imc_lkalloc 関数はロック・セットの割り当てに失敗し,値 IMC_EXISTS を返します。 [例に戻る]

  4. ロック・セットを作成してロック番号 0 を取得したプロセスが,グローバル領域を初期化します。 [例に戻る]

  5. このプロセスは,この領域の初期化が終了すると,imc_lkrelease 関数を呼び出して,ロックを解放します。 [例に戻る]

  6. この領域が初期化された後に実行される 2 番目のプロセスは,初めの imc_lkalloc 関数の呼び出しに失敗し,今度は IMC_CREATOR フラグを指定せずに,この関数を再度呼び出します。key パラメータの値が同じ (456) なので,この関数呼び出しでは,同じロック・セットが割り当てられます。 [例に戻る]

  7. 2 番目のプロセスは imc_lkacquire 関数を呼び出して,ロック・セットからロック 0 を取得します。 [例に戻る]

  8. 2 番目のプロセスは,プロセス・カウンタをアップデートし,その値を送信領域に書き込みます。 [例に戻る]

  9. プログラムの最後で,このプロセスはすべての 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 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 のファシリティに関する情報を,標準出力に表示します。出力結果には,領域やロック・セットがリストされ,次のような情報が示されます。

10.10    共用メモリ・モデルとメッセージ渡しモデルの比較

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_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 項を参照してください。