01.4 共通域のGETMAINとアンカーポインター(NAME/TOKEN)

複数のジョブで構成されるソフトウェアあるいはジョブをまたがってデータをやり取りするようなソフトウェア(例えば通信サーバー、データベースなど)では、共通域を使ってデータをやり取りすることができます。多重アドレス空間のMVSではジョブ内のメモリーはそれぞれ独立しているので、他のジョブのリージョン内領域を参照することは容易ではありません。現在のMVSではクロスメモリー・サービスも利用できますが、多くのソフトウェアは共通域であるCSAを利用する方法を採ってきました。この場合、CSAにデータを受け渡す領域を用意して、WAITとPOSTで互いの同期を取るような方法が使われます。

共通域(CSA)のGETMAIN

CSA領域をGETMAINするにはサブプール番号として241を指定します。CSAにはいくつかのサブプールがありますが、通常は241です。読み出し保護も掛けたい場合には231が利用できます。他のCSAサブプールは、ページ固定が掛かるので特別な場合でなければ利用しません。
MVSではGETMAINの代わりにSTORAGE OBTAINマクロも利用できます。どちらを利用してもCSAのGETMAINにはAPF許可が必要です。もしくはSVCルーチンとして動作するかです。

ジョブ間での同期取り

データさえ共通域に置ければ、どのジョブからもアクセスできます。したがって、データを待っているジョブにCSAにデータを書き終えたことを通知すればよいのです。POSTマクロを使うのが簡単です。相手はWAITマクロで待っています。
しかし、ここで問題が起きます。POSTしようにも相手のECBはどこにあるのでしょうか?データを受け取る相手側もECBにPOSTされた後、データをどこから持ってくればいいのでしょうか?

アンカー・ポインターを用意する

プログラム・デザインは様々なので、あくまでも一例として挙げます。空間が異なる複数のプログラムでデータをやり取りする場合、制御用も含めて必要なデータ域を1つにまとめたものを作ります。MVSではコントロール・ブロックと呼ばれます。C言語で言えば構造体です。この中に同期取りの待ち合わせに使うECBや受け渡しに使うデータ・フィールド、あるいはデータ領域へのポインターなどを入れます。そして、このコントロール・ブロック自身をCSAにGETMAINして、コントロール・ブロックの先頭アドレスを使用するプログラムが共通して参照できる場所に格納します。プログラムが共通して参照できる場所を「アンカー・ポインター」と呼びます。
上記の例では、ジョブBのプログラムがアンカー・ポインターからCOMMAREAのアドレスを持ってきています。ジョブAのプログラムは自分でCOMMAREAをGETMAINしたので、アンカー・ポインターが無くてもアドレスを知ることができます。しかしながら、それではジョブA以外のプログラムではアドレスを知りようがないので、ジョブAのプログラムは、ジョブBのプログラムがCOMMAREAのアドレスを求めることができるよう、どこかにアンカー・ポインターを用意してそこにCOMMAREAのアドレスを書き込んでおかねばなりません。どこにアンカー・ポインターを持つかはプログラム・デザインにおける腕の見せ所でもありました。

アンカー・ポインターの持ち方にはいくつかありますが、この例のように複数のアドレス空間から参照できるためにはCSAなどの共通域に用意しなければなりません。CVTUSERなど、OSのコントロール・ブロックのユーザー・フィールドはこういう時のためにあるのですが、CVTUSERはシステムで1つしかないので取り合いになります。1つのプログラムでしか使わないのであればまぁいいとしても、ユーザー・アプリケーションであることが大前提です。ベンダー製品などでは、OSのサブシステム・インターフェースで使用するSSCTを作ってチェインに繋げる方法がよく使われました。実際にサブシステムとして動く必要はなく、OSがサブシステムを識別するSSCTというコントロール・ブロックを作り、そこにアンカーポインターを設けるのです。
1つのアドレス空間内での複数タスクの場合、単純構造プログラムなら任意のモジュールにアンカー・ポインターを設けENTRYで外部参照可能にしておけば、他のモジュールからはV型定数で参照できます。動的構造プログラムならロード・モジュールを使うこともできます。データ・フィールドだけで構成されたモジュールを作りローディングしておけば、同じアドレス空間なら自由に書き込み・参照ができます。モジュールの先頭アドレスはCSVQUERYマクロで求めることができます。その他にはENQを使った方法もありました。RNAMEにアンカーとなる情報を入れておき、参照する側はOSのENQ制御表のチェインをたどってターゲットのQNAMEとRNAMEを見つけるといった方法でした。
そのようにいろいろ工夫されてきましたが、現在のMVSでは「NAME/TOKEN(名前付きトークン)」というサービスを使うのが便利です。他で使われてはいないだろうかを気にする必要もなくなりましたし、OSの制御表チェインを書き換えたりたどったりする方法ではOS側のエンハンスで構造が変わってしまうと動かなくなってしまいます。実際、ENQの制御表チェインなどは昔のMVSでは共通域(SQA)に置かれていましたが、今では構造も変わりGRSアドレス空間で管理されています。

NAME/TOKENサービスを使ったアンカー・ポインター

共通のデータ域を確保して初期設定するプログラムは、必要な設定が終わった後にアンカー・ポインターを作成するためにNAME/TOKENサービスを呼び出します。新しいトークンはIEANTCRルーチンを呼び出すことで作成することができます。複数空間で参照するアンカーポインターは、システム・レベルのトークンとして作成する必要があり、呼び出し元はスーパーバイザー・モード(またはPSWキー0から7)でなければなりません。
作成済みのトークンはIEANTRTルーチンを呼び出すことで参照できます。レベルと名前を指定してIEANTRTルーチンを呼び出せば、名前に対応したトークン値が戻されます。この例では、トークンにGETMAIN済みの共通データ域のアドレスを使っています。ジョブAでGETMAINした共通データ域のアドレスは、NAME/TOKENサービスを介してジョブBに渡すことができます。
アンカーポインターが設定できれば、後は正しく同期を取るロジックを追加すればいいのです。このサンプルではプログラムの起動順やアンカーが設定されていなかったり、相手プログラムがアクティブでない場合の処理については載せていませんが実際のプログラムでは必要になります。

空間をまたがったプログラム間通信を実装するには、CSAとWAIT/POSTによる方法以外にもクロスメモリー・サービスを利用して互いのリージョン内を直接参照する方法もあります。いずれもAPF許可がなければ作成できません。そうなると中級よりは上級レベルのプログラムでしょう。APF許可の取れないプログラムであれば、VTAMやTCPを利用した通信処理を利用する方法もあります。TCPを利用すれば同じプログラムで異なるOS間でのプログラム通信処理も可能になります。