複数のジョブで構成されるソフトウェアあるいはジョブをまたがってデータをやり取りするようなソフトウェア(例えば通信サーバー、データベースなど)では、共通域を使ってデータをやり取りすることができます。多重アドレス空間のMVSではジョブ内のメモリーはそれぞれ独立しているので、他のジョブのリージョン内領域を参照することは容易ではありません。現在のMVSではクロスメモリー・サービスも利用できますが、多くのソフトウェアは共通域であるCSAを利用する方法を採ってきました。この場合、CSAにデータを受け渡す領域を用意して、WAITとPOSTで互いの同期を取るような方法が使われます。
共通域(CSA)のGETMAIN
1 2 3 4 5 6 7 8 9 10 |
----+----1----+----2----+----3----+----4----+----5----+----6----+----7-- : L R3,LENGTH LOAD STORAGE LENGTH GETMAIN RC, OBTAIN COMMUNICATION AREA + LV=(3),SP=241,LOC=(ANY,ANY) LTR RF,RF SUCCESSFUL ? BNZ STOERROR NO, LR R2,R1 GR2 --> OBTAINED STORAGE ADDR : : |
CSA領域をGETMAINするにはサブプール番号として241を指定します。CSAにはいくつかのサブプールがありますが、通常は241です。読み出し保護も掛けたい場合には231が利用できます。他のCSAサブプールは、ページ固定が掛かるので特別な場合でなければ利用しません。
MVSではGETMAINの代わりにSTORAGE OBTAINマクロも利用できます。どちらを利用してもCSAのGETMAINにはAPF許可が必要です。もしくはSVCルーチンとして動作するかです。
ジョブ間での同期取り
1 2 3 4 5 6 7 8 9 10 |
ジョブAのプログラム ----+----1----+----2----+----3----+----4----+----5----+----6----+----7-- : LR R2,R1 GR2 --> OBTAINED STORAGE ADDR MVC 0(80,R2),=CL80'PARAMETER...' L R3,ASCBJOBB LOAD JOB-B ASCB ADDRESS POST ecbaddr,postcode, WAKES JOB-B + ASCB=(3),ERRET=CVTBRET : : |
1 2 3 4 5 6 7 |
ジョブBのプログラム ----+----1----+----2----+----3----+----4----+----5----+----6----+----7-- : WAIT ECB=ecbaddr WAITING JOB-A DATA MVC OURWORK,parameter from job-a : : |
データさえ共通域に置ければ、どのジョブからもアクセスできます。したがって、データを待っているジョブにCSAにデータを書き終えたことを通知すればよいのです。POSTマクロを使うのが簡単です。相手はWAITマクロで待っています。
しかし、ここで問題が起きます。POSTしようにも相手のECBはどこにあるのでしょうか?データを受け取る相手側もECBにPOSTされた後、データをどこから持ってくればいいのでしょうか?
アンカー・ポインターを用意する
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
ジョブAのプログラム ----+----1----+----2----+----3----+----4----+----5----+----6----+----7-- : LR R2,R1 GR2 --> OBTAINED STORAGE ADDR USING COMMAREA,R2 ADDRESS IT FOR OUR COMM AREA MVC JOBAPARM,=CL80'PARAMETER...' L R3,ASCBJOBB LOAD JOB-B ASCB ADDRESS POST JOBBECB,0, WAKES JOB-B + ASCB=(3),ERRET=CVTBRET : : : COMMAREA DSECT JOBBECB DC F'0' WAITING ECB JOBAPARM DC CL80' ' INPUT PARAMETER JOBBRSLT DC CL256' ' PROCESSING RESULT : |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
ジョブBのプログラム ----+----1----+----2----+----3----+----4----+----5----+----6----+----7-- : L R2,anchor GR2 --> OUR COMM AREA USING COMMAREA,R2 ADDRESS IT FOR OUR COMM AREA WAIT ECB=JOBBECB WAITING JOB-A DATA MVC OURWORK,JOBAPARM READ INPUT DATA : : MVC JOBBRSLT,........ WRITE OUTPUT DATA : : COMMAREA DSECT JOBBECB DC F'0' WAITING ECB JOBAPARM DC CL80' ' INPUT PARAMETER JOBBRSLT DC CL256' ' PROCESSING RESULT : |
プログラム・デザインは様々なので、あくまでも一例として挙げます。空間が異なる複数のプログラムでデータをやり取りする場合、制御用も含めて必要なデータ域を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サービスを使ったアンカー・ポインター
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
ジョブAのプログラム ----+----1----+----2----+----3----+----4----+----5----+----6----+----7-- : LR R2,R1 GR2 --> OBTAINED STORAGE ADDR USING COMMAREA,R2 ADDRESS IT FOR OUR COMM AREA MVC JOBAPARM,=CL80'PARAMETER...' * *----------------------------------* * * CREATE OUR NEW ANCHOR POINTER * * *----------------------------------* L R0,=A(IEANT_SYSTEM_LEVEL) LOAD SYSTEM LEVEL VALUE ST R0,LEVEL SET IT L R0,=A(IEANT_NOPERSIST) LOAD PERSIST OPTION VALUE + DON'T USE PERSIST NAME/TOKEN + DUE TO AUTO DELETE AT OUR ADDR + SPACE ABENDED. ST R0,PERSIST SET IT ST R2,TOKEN SET COMM-AREA ADDR IN TOKEN SPACE , MODESET MODE=SUP CHANGE US TO SUP STATE L RF,CVTPTR LOAD CVT L RF,CVTCSRT-CVT(,RF) LOAD CSRTABLE L RF,X'14'(,RF) LOAD NAME/TOKEN VECTOR L RF,X'04'(,RF) LOAD IEANTCR ENTRY CALL (15), CALL IEANTCR TO ADD COMM-AREA + (LEVEL,NAME,TOKEN,PERSIST,RETCD) ANCHOR MODESET MODE=PROB BACK TO PROBLEM STATE CLI RETCD,IEANT_OK SUCCESSFUL ? BNE NMTERROR NO, NAME/TOKEN ERROR * *----------------------------------* * * FIND TARGET ADDRESS SPACE ASCB * * *----------------------------------* L R1,CVTPTR LOAD CVT L R1,CVTASVT-CVT(,R1) LOAD ASVT USING ASVT,R1 ADDRESS IT L R0,ASVTMAXU LOAD MAXIMUM ASID NUMBER FINDASCB DS 0H TM ASVTENTY,ASVTAVAL IS THIS ASSIGNED ASCB ? BO NEXTASCB NO, IGNORE THIS ENTRY L RE,ASVTENTY LOAD CURRENT ASCB ICM RF,15,ASCBJBNI-ASCB(RE) LOAD BATCH JOBNAME POINTER BNZ *+4+4 IF NZERO, MAY BE BATCH L RF,ASCBJBNS-ASCB(,RE) LOAD STC/TSO NAME POINTER CLC 0(8,RF),=CL8'MYJOB2' IS HERE TARGET JOB ASCB ? BE *+4+4+4+4 YES, FOUND TARGET NEXTASCB DS 0H LA R1,4(,R1) LOCATE NEXT ASCB ENTRY BCT R0,FINDASCB TRY TO NEXT B NOTARGET (TARGET JOB IS NOT ACTIVE) DROP R1 FORGET ASVT LR R4,RE GR4 --> TARGET SPACE ASCB * *----------------------------------* * * WAKE WAITING OUR PARTNER JOB * * *----------------------------------* L R3,CVTPTR LOAD CVT USING CVT,R3 ADDRESS IT POST JOBBECB,0, WAKES JOB-B + ASCB=(4),ERRET=CVTBRET DROP R3 FORGET CVT : : : : RETCD DC F'0' NAME/TOKEN RETURN CODE LEVEL DC F'0' NAME/TOKEN LEVEL PERSIST DC F'0' NAME/TOKEN PERSIST OPTION NAME DC CL16'MYJOB-ANCHOR' NAME/TOKEN NAME TOKEN DC XL16'00' NAME/TOKEN TOKEN : : COMMAREA DSECT JOBBECB DC F'0' WAITING ECB JOBAPARM DC CL80' ' INPUT PARAMETER JOBBRSLT DC CL256' ' PROCESSING RESULT : |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
ジョブBのプログラム ----+----1----+----2----+----3----+----4----+----5----+----6----+----7-- * *----------------------------------* * * EXTRACT OUR COMM-AREA ANCHOR * * *----------------------------------* L R0,=A(IEANT_SYSTEM_LEVEL) LOAD SYSTEM LEVEL VALUE ST R0,LEVEL SET IT L RF,CVTPTR LOAD CVT L RF,CVTCSRT-CVT(,RF) LOAD CSRTABLE L RF,X'14'(,RF) LOAD NAME/TOKEN VECTOR ? L RF,X'08'(,RF) LOAD IEANTRT ENTRY CALL (15), CALL IEANTRT TO GET COMM-AREA + (LEVEL,NAME,TOKEN,RETCD) ANCHOR LTR RF,RF SUCCESSFUL ? BNE NMTERROR NO, NAME/TOKEN ERROR ICM R2,B'1111',TOKEN GR2 --> OUR COMM AREA BZ CMAERROR IF NOT, COMM-AREA NOT AVAILABLE USING COMMAREA,R2 ADDRESS IT FOR OUR COMM AREA : : : * *----------------------------------* * * WAIT INPUT FROM PARTNER JOB * * *----------------------------------* WAIT ECB=JOBBECB WAITING JOB-A DATA MVC OURWORK,JOBPARM READ INPUT DATA : : MVC JOBRSLT,........ WRITE OUTPUT DATA : : : : RETCD DC F'0' NAME/TOKEN RETURN CODE LEVEL DC F'0' NAME/TOKEN LEVEL NAME DC CL16'MYJOB-ANCHOR' NAME/TOKEN NAME TOKEN DC XL16'00' NAME/TOKEN TOKEN : : COMMAREA DSECT JOBBECB DC F'0' WAITING ECB JOBAPARM DC CL80' ' INPUT PARAMETER JOBBRSLT DC CL256' ' PROCESSING RESULT : CVT DSECT=YES CVT IEANTASM , NAME/TOKEN SERVICE DECLARES |
共通のデータ域を確保して初期設定するプログラムは、必要な設定が終わった後にアンカー・ポインターを作成するためにNAME/TOKENサービスを呼び出します。新しいトークンはIEANTCRルーチンを呼び出すことで作成することができます。複数空間で参照するアンカーポインターは、システム・レベルのトークンとして作成する必要があり、呼び出し元はスーパーバイザー・モード(またはPSWキー0から7)でなければなりません。
作成済みのトークンはIEANTRTルーチンを呼び出すことで参照できます。レベルと名前を指定してIEANTRTルーチンを呼び出せば、名前に対応したトークン値が戻されます。この例では、トークンにGETMAIN済みの共通データ域のアドレスを使っています。ジョブAでGETMAINした共通データ域のアドレスは、NAME/TOKENサービスを介してジョブBに渡すことができます。
アンカーポインターが設定できれば、後は正しく同期を取るロジックを追加すればいいのです。このサンプルではプログラムの起動順やアンカーが設定されていなかったり、相手プログラムがアクティブでない場合の処理については載せていませんが実際のプログラムでは必要になります。
空間をまたがったプログラム間通信を実装するには、CSAとWAIT/POSTによる方法以外にもクロスメモリー・サービスを利用して互いのリージョン内を直接参照する方法もあります。いずれもAPF許可がなければ作成できません。そうなると中級よりは上級レベルのプログラムでしょう。APF許可の取れないプログラムであれば、VTAMやTCPを利用した通信処理を利用する方法もあります。TCPを利用すれば同じプログラムで異なるOS間でのプログラム通信処理も可能になります。