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

プログラムの外側に領域を確保して使用する際、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になってしまうのでどこから調べていいのか途方に暮れてしまうかも知れません。

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

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