01.1仮想記憶域を獲得する(GETMAINとFREEMAIN)

By 神居 - Posted: 2008/11/14 Last updated: 2009/12/03 - Leave a Comment

プログラムで使うデータ領域には、あらかじめその大きさを固定できないものもあります。例えばデータセットを読み込む処理で、レコードを読み込む領域の大きさを実際のデータセットのレコード長に合わせるような場合です。考え得る最大の長さでプログラム内に領域を用意するのも1つの方法です。しかしほとんどが100?200バイトの範囲で収まるが、たまに30000バイトなどと言う大きなものも取り扱う、と言ったケースでは、その「たまに」のケースのために無駄に大きい領域を定義しなければなりません。いくら今では仮想記憶はふんだんに使えると言っても、このようなプログラムは考え物です。

このような場合、プログラムは必要なときに、必要な量の仮想記憶域(ストレージ)をダイナミックに獲得して使うことが出来ます。これがMVSのGETMAINおよびFREEMAINサービスです。GETMAINはストレージを獲得し、FREEMAINはストレージを解放します。返却と言ってもいいでしょう。必要な時に必要な領域をOSから借りて、使い終わったら返す、のです。


72バイトのレジスター保管域をプログラムの外に確保する

最も基本的なGETMAINとFREEMAINマクロの例です。Rは16MBラインの下の領域(以下、24ビット域と記します)の無条件要求/解放を行います。LVパラメーターで獲得/解放する長さを指定します。FREEMAINマクロではAパラメーターで解放する領域のアドレスも指定します。
無条件要求なので獲得や解放に失敗するとプログラムはABENDします。例えば指定した領域が大きすぎて獲得できない、獲得されていない領域を解放しようとした、などです。GETMAINは成功するとGR1に獲得された領域の先頭アドレスを返します。GR0,GR14,GR15はマクロ内あるいはサービスルーチン側で作業用に使用されるため内容は破壊されます。GR2?13はそのままです。
このGETMAIN/FREEMAINも含め多くのサービスではGR0,1,14,15は作業レジスターとして使用されるか、サービスの処理結果(復帰コードや獲得された領域のアドレス等々)を通知する目的で使われます。このことは共通して覚えておくといいでしょう。GR14などはそのままの値が保持されるものもありますが、それはたまたまです。今は値がキープされていても、次のバージョンでは使われてしまうこともあり得ます。マニュアルにはその辺りもきちんと明記されていますから必ず確認してください。

このサンプルはリエントラント(再入可能)構造のプログラムを作る場合に使われるものです。リエントラントであることが要求されるプログラムでは自分のプログラム内にデータを書き込むことができません。そのためレジスター保管域も自分のプログラム内には定義できません。サンプルでは簡略化してありますが、実際には入口点ではGR0やGR1にパラメーターアドレスが入っていたり、復帰時にはGR15にリターンコードを返す必要があったりします。これらのリンケージレジスターGR0,1,14,15はマクロを発行されると内容が壊れてしまうため、必要に応じて別レジスターに一旦コピーしておくなどの工夫が必要です。どこにどういう命令を入れればよいかなどはご自身で考えてみて下さい。入口点でのGR13は呼び出し元レジスター保管域を指しますが、これは自分のプログラム内領域ではありませんから、リエントラントかどうかに関係なくそのまま使用できます。呼ばれたプログラムが最初に何をなすべきかはすでにご存じですね。それがわかれば壊されてしまうレジスターをどこでコピーすればよいかすぐにわかるでしょう。


OS出口ルーチンで使う作業用領域をプログラムの外に確保する

同じ無条件要求ですがRではなくRUパラメーターを指定しています。RU要求ではLOCパラメーターで獲得したいストレージの位置(16MBラインの上下)を指定できます。SPパラメーターはストレージを割り当てるサブプール番号です。(サブプールについてはここに簡単な説明があります「サブプールと記憶保護キー」)SP=230はPVTで、高位のアドレスから割り当てられる領域です。一般に使用されるSP=0は低位のアドレスから割り当てられます。OSの出口ルーチンでPVTを使う時SP=230はよく使われるサブプールです。ちなみに128以上のサブプールはAPF許可やスーパーバイザーモードのプログラムでなければ利用できません。

LOCパラメーターは省略すればプログラムのアドレスモードに合わせて適切な領域が獲得されます。24ビットモードなら16MB未満、31ビットモードなら16MB以上です。31ビットモードの時、マニュアルには16MBの上下いずれかからとありますが、現実には16MBの上から優先して割り振られます。仮に16MB以上の領域が一杯であれば、下からの割り振りになるのでしょうが、現実に16MB以上の領域が一杯になるような状況では別のエラーが先に起こるでしょう。
獲得するストレージ位置をOS任せにしてもいいのですが、APIの仕様でアドレスモードに関係なく16MB未満の領域を要求するものや、両方のアドレスモードを状況によって切り替えるようなプログラムでは、LOCパラメーターでストレージ位置を明示的に指定します。その方がソースやリストを見たときにわかりやすくなります。LOC=(BELOW,ANY)は仮想記憶上は16MB未満、実記憶は16MBの上下いずれでもよいことを示し、LOC=(ANY,ANY)は仮想記憶も実記憶も16MBの上下いずれでもよいことを示します。一般のプログラムは実記憶をアクセスすることはありませんから、24ビットモードのプログラムでは仮想記憶は16MB未満でなければなりませんが、リアルメモリーはどこであってもかまいません。MSPとVOS3ではBELOW/ANYのキーワードは同じですが構文に若干の違いがあるのでマニュアルを参照して下さい。なおz/OSではBELOW/ANYの代わりに現在では24/31と書くことが推奨されています。これは実記憶が64ビットに拡張されていることに関連します。

最初の例でリエントラントプログラムのレジスター保管域をプログラム外に獲得する方法を紹介しました。このサンプルではもう少し拡張して考えます。プログラムで書き込みを行うのはレジスター保管域だけとは限りません。その他にも処理に使うさまざまなデータをメモリーに書き込みます。S/370アーキテクチャーでは1つのベースレジスターでアドレスできるメモリーの大きさは4KBあります。レジスター保管域はGR13でポイントしますが、GR13をレジスター保管域だけの目的で使ってしまうと4KBアドレスできるのに先頭の72バイトしか使いません。もったいないことです。そこで先頭の72バイトをレジスター保管域にして、続く残りの領域をプログラムの作業用領域とすれば、最大4024バイトまでの領域を同じGR13でアドレスすることができます。GR13はレジスター保管域のアドレスを格納するものでそれ以外の目的に使ってはならないと、頑なに信じていませんか?。レジスター保管域の後ろにプログラムの作業領域を置いても、GR13が72バイトのレジスター保管域のアドレスを示すことに変りありません。特にリエントラントプログラムではレジスター保管域はプログラム外に置くのが鉄則ですから、どうせならその後ろの領域も積極的に活用しましょう。


31ビット領域にバッファ用ストレージを獲得する

RCは条件付き要求です。条件付き要求とは指定した長さのストレージが獲得できない時や指定した領域が解放できない時に復帰コードで通知されます。プログラムは復帰コードを判定することで、要求したストレージが獲得できたか、解放できたか、がわかります。復帰コードはGR15で通知され、GETMAIN/FREEMAIN共に成功すれば0が返ります。0以外の時は要求が失敗したことを示します。

BNDRYパラメーターは割り当てる領域の先頭アドレスをどのように境界合わせするかの選択です。DBLWD(8バイトバウンダリー)とPAGE(4KBバウンダリー)があり、デフォルトはDBLWDです。

サンプルではFREEMAINにはRUを使っています。もちろんRCでもかまいません。ただFREEMAINの場合、指定した領域が返却できないのはシステムやJCL(REGIONサイズ)の問題よりは領域アドレスやサブプール番号を誤って指定していたりするケースがほとんどですから、基本的にはプログラムのバグです。RU要求ならばこのような時必ずABENDしますから、それでバグがあることがわかります。ABENDを嫌ってRC要求にしても、復帰コードを判定していないなど、きちんとエラー処理をしないのならば、結果として領域の解放漏れを見逃してしまうことにもなります。


サブプールの解放

一般のプログラムではあまり使われませんが、FREEMAINにはサブプールの解放と言う機能もあります。特定のサブプールに属するGETMAIN済みストレージをまとめて解放します。例えばサブルーチンの処理結果を返す領域をサブルーチン側で用意し、解放はメインルーチン側で行うような場合、アドレスや長さを管理しなくともサブプール単位でまとめて解放することができます。
サブプール単位の解放はTPモニターなどアプリケーションプログラムを制御する側にとっては便利な機能です。


STORAGEマクロ(MVSのみ)

MVSではGETMAIN/FREEMAINに代わり、OBTAINマクロを使用することもできます。GETMAIN/FREEMAINはSVC命令でAPIが呼び出されますが、OBTAINではPC命令によるクロスメモリー機能が使われます。一般のプログラムではGETMAIN/FREEMAINでかまいませんが、SVC命令を発行できないモードで走行中のプログラムやアクセスレジスターモードで走行中のプログラムではOBTAINマクロを使う必要があります。SVC命令を発行できないモードで走行中のプログラム(SRBルーチンやタイプ1SVCルーチンなど)では従来はブランチ命令によって、OSのGETMAIN/FREEMAINルーチンを直接呼び出す必要がありましたが、ローカルロックの保持など、OSのSVCハンドラー並みの環境設定を行う必要があります。STORAGEマクロによる方法はコーディングも容易で、どのようなモードのプログラムでも共通に利用できるストレージサービスです。


ワンポイント知識

Posted in .基礎編 • • Top Of Page