- 「メインフレーム・コンピューター」で遊ぼう - http://www.arteceed.net -

サブプール・ゼロ(0)の落とし穴

             GETMAIN R,LV=2000              OBTAIN 2KB STORAGE
             LR    R6,R1                    GR6 ---> STORAGE ADDRESS
             :
             :
    

プログラムの外側に領域を確保して使用する際、GETMAINマクロで長さだけを指定すると、領域はサブプール0に獲得されます。これ自体は何も問題ありませんし、使い方として誤ってもいません。ただ、獲得された領域を使用する際に、バグって領域を超えてアクセスした場合、レジスター保管域を壊してしまう可能性があります。特に、プログラム内で最初に発行されるGETMAINで獲得された領域を使用する場合です。

MVSは、タスクで最初に実行されるプログラム(ATTACHされるプログラム)が使用するレジスター保管域(RSA)の領域をサブプール0に確保します。MVSがGETMAINするRSA領域は144バイトですが、その後ろにEXECステートメントのPARMパラメーターで指定された文字列格納領域が続きます。PARMパラメーターを指定しなくても、パラメーター・リスト領域と長さ格納域は必要になるので、最低でも160バイトは使用されます。
既にサブプール0の最初のページが切り出された状態で、サブプール指定なしのGETMAINが発行されると、要求された領域長が未使用領域長以下(例えば、2000とか3000バイト)なら、最初のレジスター保管域と同じサブプールが使われます。MVSはページ内の後方から領域を割り当てるため、プログラムが確保した領域は、下図に示すように、MVSが最初に獲得したレジスター保管域の直前に配置されます。
そのため、プログラムにバグがあり領域を超えて書き込みを行うようなことがあると、レジスター保管域内を破壊してしまうことになります。レジスター保管域を超えてもなお書き込みを続けようとするとページの末尾に達します。その後ろのページが割当て済みであれば、そこを破壊するか、記憶保護キー不一致(S0C4)でABENDさせられます。割当て済みのページが続いていなければ、ページ変換例外(S0C4)でABENDさせられます。
後続の領域を破壊した時点でABENDすればまだ原因は追跡し易いですが、別の処理や別プログラムで使用している領域を壊すだけ壊して抜けてしまうと、その後全く別の部分でエラーになることが予想できます。例えば、呼び出し元へ戻る際にABENDしたり、誤った内容でレジスターを復元してしまう、といったものです。そのような場合のデバッグはかなり面倒です。(例えば、レジスター保管域をNULLでクリアーしてしまうと、レジスター復元後はGR13以外のすべてのレジスターが0になってしまうので、どこから調べていいのか途方に暮れてしまうかも知れない)

    ページの先頭 +----------------------------+
                 I  (未使用領域)              I
                 I                            I
                 I                            I
                 I                            I
                 I                            I
                 I                            I
                 +----------------------------+
                 I  プログラムが獲得した      I
                 I  領域                      I  I
                 I                            I  I
                 I                            I  I
                 +----------------------------+  I
                 I  MVSが獲得した             I  I  壊してはいけないところも
                 I  レジスター保管域          I  V  簡単に壊せてしまう…
                 +----------------------------+
                 I  MVSが獲得した             I
                 I  PARMパラメーター格納領域  I
    ページの末尾 +----------------------------+
    

このようなことは、0以外のサブプールを明示すれば回避できます。MVSは、例え未使用領域が残っていても、同じページを複数のサブプールで共用するようなことはしません。従って、1から127のいずれかのサブプールを明示すれば、MVSが獲得したレジスター保管域やPARMパラメーター領域と同じページに割り当てられることはありませんし、これらの領域よりも高位のアドレス領域が割り当てられるので、レジスター保管域やPARMパラメーター領域の後ろにプログラムで使用する領域が割り当てられます。

             GETMAIN R,LV=2000,SP=1         OBTAIN 2KB STORAGE ON A SEPARATE
                                            PAGE FROM MVS USED AREA
             LR    R6,R1                    GR6 ---> STORAGE ADDRESS
             :
             :
    

とは言え、こんなことまで気にしながらプログラム・コードを考えるのは面倒です。実際には、プログラムを作るための知識ではなく、「こういうことも起き得るのだ」と言うデバッグする時の知識の1つとして知っておけばいいでしょう。