ユーティリティ

By kamii - Last updated: 水曜日, 11月 19, 2008

MVS,MSPおよびVOS3では多くのユーティリティ・プログラムに互換があります。プログラム名は違っても機能、JCL、制御文などはまったく同じかほとんど同じと言っていいものが多いです。特に古くからある伝統的なユーティリティは元々がMVS互換のためかほとんど同じです。ディスクのバックアップなどに使用するユーティリティなどは後年になってそれぞれのメーカーから独自のものが出てきていますから同等ではあっても互換はありません。
同じ(互換)ユーティリティかどうかはMVSのユーティリティ・プログラム名で実行できるかどうかでおおよその見当がつきます。MVSのプログラム名の別名が付いていれば互換ユーティリティと考えていいでしょう。


Filed in OSの互換性

07.REXX 外部コマンド

By takao - Last updated: 水曜日, 11月 19, 2008
REXXから外部コマンドを発行する方法について、書きます。
もっとも乱暴な話をすると、REXXプログラムの中に外部コマンドを書くと実行されます。たとえば、
/* REXX */
STATUS
say RC

TSOのSTATUSコマンドが実行されると思います。ただし、暗黙の前提をフルに使っています。
暗黙の設定を順に説明します。STATUSは変数として値をもっていないので、’STATUS’という値をもちます。これはREXXコマンドにはないので、REXXは外部環境に渡します。外部環境がTSOであった場合、無事にSTATUSコマンドが実行されます。RCは外部コマンド実行後にリターン・コードが入る特別な変数名です。

変数の暗黙の値を利用するのは、わかりにくくなる原因なので、”STATUS”というように”で囲むことが強く推奨されています。

ここで外部環境とあっさり書きましたが、状況は簡単でないことも多いでしょう。次のように環境が重なっている場合が普通かと思います。
REXX
ISPF
TSO
…..
この層になっている環境において、どれに対してコマンドを渡すか明示しておいたほうが安心です。
そのためには、ADDRESSコマンドを使います。(とはいいながらも、ADDRESS以降にどの環境をどういう名前で指定するかは決まっているので、事前にマニュアルで調べることが必要です。)
たとえば、ISPFが動作している環境でISPFに対してコマンドを出す場合、
ADDRESS ISPEXEC ‘SELECT service’

MVSが提供しているサービスを利用する場合にも、ADDRESSを使います。
ADDRESS MVS command …..
というように指定します。
ここで、commandはこのプログラム内だけで使うスタックを定義する、NEWSTACK,QSTACK,DELSTACK,DROPSTACK, MAKEBUF,DRIPBUF,QBUF,QELEM)。 実行モードを制御するEXECUTIL, I/OサービスをしてくれるEXECIOなどがあります。

ここではとくに、I/OサービスのEXECIOをサンプルとしてみてみましょう。今までの勉強の確認にもなります。

/* REXX */
"ADDRESS TSO ALLOC DD(INDD) DA('SYS1.PARMLIB(IEASYS00)') SHR REUSE"
"ADDRESS MVS EXECIO * DISKR INDD (FINIS"
"ADDRESS TSO FREE DD(INDD)"

DO QUEUED()
 PARSE PULL record
 SAY record
END
EXIT

2行目は、TSO CLISTでおなじみのAllocateコマンドです。3行目は、MVSサービスでREXXに提供されているEXECIOです。*はレコードを全部読むという意味で、次のDISKRは読み取りを意味します。INDDはAllocateコマンドで設定したDD名です。FINISは読み終わったらファイルをクローズするという意味です。読み取られたレコードはすべてスタックにおかれています。
つまり、スタックにファイルを読み取りたかったら、このままの形式で(もちろんDD名だけは考慮が必要ですが)読み取れます。
4行目は、TSO CLISTでおなじみのFREEコマンドです。アロケートしたファイルを開放します。

次のDO~ENDはスタックにある限りループします。QUEUED()はスタックの大きさを返します。
スタックからデータを取りだすために、PULLし、データをrecorという変数にPARSEで割り当てます。SAYで表示することの繰り返しです。
単純ながらプログラムらしくなってきましたね。

次にEXECIOの別の使い方を紹介してみます。ここでは、ADDRESSは面倒なので省略します。多くの場合、うまく外部コマンドは見つかり実行されます。


/* REXX */
"ALLOC DD(INDD) DA('SYS1.PARMLIB(EIASYS00)') SHR REUSE"
"EXECIO * DISKR INDD (STEM rec. FINIS"
"FREE DD(INDD)"

DO i=1 TO rec.0
 SAY rec.i
END
EXIT

EXECIOにおいて、STEM rec. が加わってます。これで配列rec.にファイルが読み込まれます。
配列からデータを取り出すために、iというカウンターでループしてデータを取り出します。配列のrec.0には、要素数が入っています。

以上で基本的な外部コマンドの使い方を終わります。
Filed in REXX入門

02.2インターバルタイマー(STIMER)

By kamii - Last updated: 水曜日, 11月 19, 2008

時間に関する機能には、日付と時刻の取得の他にインターバルタイマーがあります。間隔計時機構とも呼ばれ、一定時間おきに行う処理を作る際や一定時間プログラムの実行を止める場合などに利用されます。


10秒間プログラムの実行を停止する

一定時間プログラムの実行を止めるにはSTIMERサービスのWAIT機能を使います。STIMERマクロの最初の定位置パラメーターにWAITを指定し、続けて待ち合わせ時間を定義したパラメーターを指定します。サンプルでは待ち合わせ時間を2進整数で指定しています。1/100秒単位で指定でき、1は1/100秒、100は1秒となります。STIMERマクロ発行後、OSサービスルーチン内で指定した時間が経過するまで待ち状態に入ります。指定した時間が経過するとプログラムに戻ってきます。

サンプルの処理内容は、指定した区分データセットのメンバーがISPFエディターで編集中かをENQマクロでテストします。編集中であればENQが失敗するので、その事をWTOマクロでコンソールにメッセージで通知して、10秒間待ち状態に入ります。10秒経過後、再びENQをトライします。このサイクルで6回繰り返し、都合1分間リトライします。ENQに成功した場合と、リトライ回数をオーバーしたらプログラムは終了します。
ENQマクロによる排他制御が獲得できない時、一定回数リトライすることはよく行われますが、人間の操作が絡む資源の場合、単にループするよりはSTIMERマクロを組み合わせて一定時間待ってからリトライするのが一般的です。


指定した時刻になったら通知してもらう

目覚まし時計プログラムです。一定時間の経過ではなく、指定した時刻になったら起き上がるサンプルです。今度はWAITではなくREALを指定しています。REALは実時間の経過を通知する機能です。TODパラメーターは間隔時間ではなく時刻を指定するパラメーターです。MVSではTODの代わりにLTパラメーターでもかまいません。
運用の自動化ソフトなどを使えば必要ありませんが、18:00になったらオンラインシステムを終了させる、21:00になったらバックアップジョブを実行させる、など決まった時刻になったら行う処理を自動化させるためには目覚まし時計が必要です。


10秒ループしたら自分で自分をキャンセルする

実時間ではなくCPU時間によるインターバルタイムの計測です。REALの代わりにTASKと指定します。間隔時間の指定方法はREALもTASKも同じです。このサンプルではCPUタイマー単位(約26.04166マイクロ秒)で指定しています。約10秒です。わかりにくいので普通にBINTVLで指定してかまいません。
サンプルの処理は一定のCPU時間を使い切ったらプログラムをABENDさせる例です。タイマーをセットしてから、わざと無限ループに入ります。指定した時間のCPUを使い切ると、TIMREXITに飛び込んできます。


サンプルではタイマー出口内で大した処理をしてませんので簡単に書いてますが、出口ルーチン内からメインライン側のデータ領域などを直接参照・更新したい場合は、上記のようなハウスキーピングをすればいいでしょう。メインライン側のベースアドレスを同じベースレジスターに再設定します。STIMER出口ルーチンの規約では、汎用レジスター0?12の内容はSTIMERマクロ発行時の内容とは規定されていません。仮にGR12にプログラムのベースアドレスが入っていたとしても、それはたまたまです。マニュアル通りGR0?12は壊れていると言う前提で再設定しましょう。STIMERに限らずすべてのマクロの出口ルーチンについて言えます。マニュアルに記載されている出口ルーチンのレジスター規約を必ず確認してそれに従います。

STIMERで設定したタイマーは設定時間が経過して出口で通知されると無効になります。再びタイマーによる時間測定が必要なら改めてSTIMERマクロを発行する必要があります。STIMERマクロは必ずしもメインルーチン内で出す必要はありません。出口ルーチン内でSTIMERマクロを発行してタイマーを再設定してもかまいません。


タイマー測定のキャンセル

TTIMERマクロを使うと一度設定したタイマーの残り時間を調べたり、タイマー計測をキャンセルすることができます。
サンプルでは10秒の実時間タイマーを設定してから、5命令×一千万回で計五千万回の命令を実行します。その後TTIMERで設定したタイマーを取り消します。GR0にタイマーの残り時間がCPUタイマー形式(約26.04166マイクロ秒単位)で返されます。サンプルではタイマー出口は意味を持たないのでBR 14命令で何もせずに復帰しています。
タイマーを設定している常駐型のプログラムなどでは、オペレーターコマンドなどによって処理を終了する時、TTIMERで不要になったタイマー設定を取り消します。TPモニターなどユーザーアプリケーションを自分の制御で実行するようなソフトでは、ユーザープログラム開始前にTASK指定のSTIMERでCPUタイマーを設定し、ユーザープログラムが終了した時点でそれを取り消したりします。

STIMERサービスは1つのタスクで同時に複数使用することはできません。STIMERサービスによるインターバルタイマーの設定は1つしか出来ないのです。同じタスクで複数のインターバルタイムを計測するためにはSTIMERM(MSPではSTIMERE、VOS3では公開されていません)マクロを利用します。MSPのSTIMEREサービスでは経過時間の通知に出口ルーチンではなく、ECBポストによる方法も選択できます。

Filed in .基礎編

データセットをファイル転送可能な形式に変換する(TRANSMITとIEHMOVE)

By kamii - Last updated: 火曜日, 11月 18, 2008

固定長レコードのデータセットであればそのままファイル転送することができますが、可変長/不定長レコードの場合は相手側転送ソフトがメインフレームのレコード形式に対応していないと正しく転送できません。
TSOのTRANSMIT/RECEIVEコマンドを使うと、順次または区分データセットであればレコード形式に関係なく、データセットをRECFM=FB,BLKSIZE=3120,LRECL=80の形式に変換し、元の形式に復元することができます。TRANSMITで変換してから転送し、転送先でRECEIVEで復元すればあらゆるデータセットを転送することができます。VSAMデータセットも一旦REPROで順次データセットにアンロードすればTRANSMITに掛けることができます。

FTPなどでの転送の場合、TRANSMITコマンドはデータの変換に過ぎませんが、NJEで接続されたz/OS同士、VSEやVMなどのIBMシステムであれば同時に転送が行われます。
※転送処理自体はNJEによって行われる。NJEで接続されていなければ、FTPや3270エミュレーターのファイル転送などを利用することになる。


TSO/TRANSMITコマンド


IEHMOVEユーティリティー

Filed in ありがたいサンプルJCL

04.1コンソールまたはSYSLOGにメッセージを出す(WTOとWTL)

By kamii - Last updated: 火曜日, 11月 18, 2008

プログラムで起きていることを状況に応じてメッセージで伝えることは重要です。特にエラーが起きてオペレーターの操作や判断を必要する場合、適切なメッセージはシステムの運用面で大きな利益をもたらします。どのような時にどのようなメッセージを出すかは、プログラムのロジックと並びデザイナーの腕の見せ所でもあります。MVSにはコンソールへメッセージを出したり、オペレーターからの操作や応答を受け取るためのAPIも用意されています。これらはコンソールを操作するオペレーターとプログラムの間の通信機能でもあります。


コンソールへメッセージを出力する

WTOマクロはコンソールへメッセージを出力します。世の中のメインフレーム・アセンブラープログラマーに「初めて使ったOSのAPIマクロは何ですか?」とアンケートを取れば恐らく1位、少なくとも3位内には入るであろう、と言うぐらいポピュラーなマクロです。
使い方もいたって簡単で、マクロ名に続いて出したいメッセージテキストを”で囲めばOKです。テキストが長くて1行に書ききれないときは、71桁目まで目一杯書いて、72桁目に何か文字を置き、次の行の16桁目から続けます。16桁目が空白でもそのまま空白文字として扱われます。

残りの2例は、メッセージテキストの中の文字を置換えて出力したい場合に用いられる方法の1つです。実戦では固定のメッセージテキストだけでなく、メッセージの中にプログラムの処理によって内容が変わる値などを埋め込みたいことがよくあります。このような場合はリスト形式と実行形式のWTOマクロを組み合わせます。再入可能プログラムではモジュール内のWTOパラメーターを直接変更できないので、モジュール外の作業域に一旦コピーして、そちら側を修正して使用します。
わざと同じENQを2回出し、それぞれのENQサービスの復帰コードをメッセージ中のRC=??の??部分に埋め込んでコンソールに出力するものです。単にENQ ERRORと出すよりはるかに実用的です。復帰コードの値がわからなければ対処のしようがありません。サンプルでは簡単にするために10進数に変換していますが、16進数文字への変換なら、マニュアルのコード表示と一致するのでさらにわかりやすくなります。

メッセージテキストはWTOパラメーターの+4バイト目から始まります。アセンブルしてマクロの展開形を見ればすぐにわかります。本来ならOSマクロの中を、直接変位を指定して場所をポイントするのは決していいことではないのですが、WTOのような基本的なマクロはその内容が変わることは現実的にありませんし、サンプルの方法は古典的ではありますが、昔から多くのプログラムで行われている、言わば標準的・伝統的な方法です。


可変長のメッセージテキストを出力する

可変長のメッセージテキストを出力する例です(※MVSでのみ利用できる方法)。
TEXTパラメーターでコンソールへ出力するメッセージテキストの格納アドレスを指定します。メッセージ領域の先頭2バイトはメッセージテキストの長さです。メッセージ長フィールドの長さは含みません。長さに続いてメッセージテキストを格納します。

MSPおよびVOS3で可変長メッセージテキストを扱う場合、リスト形式のWTOマクロを使ってWTOパラメーターがどのように作成されるかをアセンブルリストで確かめます。それに合わせてプログラムでメッセージテキストをWTOパラメーターを作り、実行形式のWTOマクロでメッセージを出力します。

メッセージテキストを’’で囲む従来からあるWTOパラメーターは上図のフォーマットになっています。先頭2バイトにメッセージテキスト長に4を加えた値を設定します。DESCまたはROUTCDEパラメーターを指定した場合、メッセージテキストの後ろに設定されますが、その長さは先頭のレングスフィールドには含まれません。パラメーターはワード境界に合わせます。


メッセージの種類と宛先を指定する

メッセージの種類や宛先を指定することもできます。DESCパラメーターはメッセージの種類(性質)を表します。通常は省略してかまいませんが、指定するなら、一般のプログラムであれば6または7でいいでしょう。OSの出口ルーチンなどで重大なエラーを検出して、オペレーターにメッセージで伝えるような時、スクロールされてメッセージが流れないようにするにはDESCコードとして11を指定します。DESCコード11を指定した場合、WTOマクロ完了時にGR1に返されるメッセージ識別番号を保存します。この識別番号を使えばDOMマクロによってメッセージを削除することが可能です。DESCコード11のメッセージはプログラムがDOMマクロを使うか、オペレーターがコンソール操作(MVSの場合、K C,A,idコマンド)によってしか消されることはありません。

ROUTCDEパラメーターはメッセージの宛先を表します。これも通常は省略してかまいませんが、一般プログラムが指定するなら2がいいでしょう。MVSではSYSLOGの3?9桁目がメッセージのROUTE CODEを示します。16進表記ですが左ビットからROUTE1,2,3,4,5,6,…,28となっています。ここを見て他のソフトウェア製品などがどのようなROUTCDEを使っているかを参考にするのも1つの方法です。FFFFFFFFとなっていたらROUTCDEは省略されています。
DESCとROUTCDEのコードそのものの詳細な解説は「MVS 宛先コードおよび記述子コード」マニュアルに記載されています。コードの用途はMSP/VOS3でも基本的に同じです。


SYSLOGへメッセージを出力する

コンソールへの表示は不要だがシスログには残したい、と言う場合もあります。そのような時はWTLマクロをWTOマクロの代わりに使うことができます。なお現在IBMではWTLマクロに代わり、MCSFLAG=HRDCPYを指定したWTOマクロによる方法を推奨しています。
MCSFLAG=HRDCPY指定のWTOを使った場合、SDSFのLOG機能やECSなどの仮想コンソールプログラムを使用すると、上記の方法ではメッセージが表示されることがあります。これはその仮想コンソール機能が純粋にコンソールとしてではなく、SYSLOGをベースにメッセージを表示しているからです。本当にコンソールには出ていないかを確認するには実コンソールを見て下さい。


補足

Filed in .基礎編

03.2資源の逐次化その2(SETLOCKとCS,CDS命令)

By kamii - Last updated: 月曜日, 11月 17, 2008

システムリソースの逐次化

MVSは制御表のキューの操作や、数ビットあるいは数ワードに対して連続的な変更をしている間、それらのメモリー領域が逐次化されることを必要とする処理が随所にあります。これらの排他制御を行うのがロック機能です。ロックはENQ/DEQと異なり資源に自由な名前を付けることはできません。代わりにロックワードと呼ばれるフルワード領域を資源毎に用意し、逐次化する資源を分類します。
ロックはSETLOCKマクロを使って獲得と解放を行うことができます。サンプルに示したのはローカルロックと呼ばれる、アドレス空間に固有の資源を逐次化する際に用いられるロックです。一般のプログラムがロックを使うことはありませんが、MVS自身は逆にENQ/DEQではなくロック機能を使って、コントロールブロックの変更やチェインの追加・切り離し、など制御に必要な逐次化を行っています。
OS以外のプログラム、特にアプリケーションが排他制御を行う目的でSETLOCKマクロを使うことはまずありません。しかしながらGETMAIN(BRANCH=YES)、POST(BRANCH=YES)、CHANGKEYなど一部のAPIで呼び出し時の環境要件としてローカルロックの保持を求められる場合があり、そのために使うことはあります。ただしそのようなAPIはSRBルーチンなど、SVC命令の発行が許されないような状況でOSのサービスを受けるために使用する特別なものです。

ロックを使えば複数のメモリー領域(例えば2000番地からの1ワード、3000番地からの2ワード、4000番地の1ビット)を続けて変更するような時、すべてのフィールドの変更が終了するまでそれらのメモリー領域の変更を逐次化することができます。しかし1ワードあるいは連続した2ワードの領域の内容を変更するだけならCPU命令によって逐次化することができます。1バイトの領域であっても前後のバイトを含めて1ワードの領域として逐次化することができます。


フルワードカウンターの更新

CS命令は第1オペランドで指定したレジスターの内容と第2オペランドで指定したフルワード領域の内容を比較して、一致していれば第3オペランド(r3レジスター)で指定したレジスターの内容を第2オペランドで指定したフルワード領域に書き込みます。一致していなければ主記憶の内容を第1オペランドで指定したレジスターにロードし直し、主記憶の内容は変更されません。実行結果は条件コードで判定できます。
この時CS命令は参照する主記憶域に特別な操作を行います。それが逐次化(Serialization)あるいはインターロック(Interlock)と呼ばれる動作です。CS命令は主記憶からデータを読み出し、比較し、書き込む、と言う一連の動作を行います。読み出し?格納までの間、他のCPUから同じ主記憶アドレスへの参照は禁止されます。この機能が無いと、マルチCPU環境では読み出してから格納するまでの一瞬の間に同じ領域が書き換えられる可能性があります。1つの同じ命令ですら読み出しから格納までには他のCPU動作が入り込む隙があるのですから、L命令とST命令の2つで同じ事をやったら壊れてしまうタイミングはもっと増えてしまいます。

システムプログラムでは複数の実行単位(マルチタスクはもちろん、シングルタスクでもAPIの非同期出口ルーチン機能を使う場合も複数の実行単位となる)で構成されるプログラムで同じ領域を更新する場合は、プログラムを動かすプロセッサーがマルチCPUかどうかに関係なく、CS命令を使用してフィールドを更新することをまず考えます。

最初の例はCS命令を使わない方法です。普通はこう考えます。CTRWORDフィールドを変更するプログラムが1つのタスクでのみ動くなら問題ありません。しかし同じプログラムが複数のタスクで動く場合はCTRWORDフィールドは競合します。命令のリファレンスには逐次化機能は2つ以上のCPU間で主記憶領域を排他制御するための仕組みと解説されていますから、CPUが1つなら大丈夫か?と思えますが例示のロジックの場合、CPUの数は関係ありません。
シングルCPUのプロセッサーでも、タスクAがLA命令で値を更新し、ST命令を実行しようとしたタイミングでI/O割込みなどが発生するとST命令の実行直前でプログラムは中断されます。割込み処理が終われば、プログラムの実行は再開されますが、必ずしも次にまたタスクAが実行される保証はありません。同じプログラムを動かすタスクBにディスパッチが移るかも知れません。そうなるとCTRWORDはタスクBによって更新されます。その後タスクAが再開されるとST命令によって上書きされますが、その内容はタスクBで更新した値が反映されません。これはCPU間の主記憶の競合の問題よりは排他を掛けずに主記憶フィールドを読み出し、書き込む、と分割した動作でやっているからです。
少し工夫して2番目のように改良できますが、BNE命令とST命令の間に割込みが入れば同じことです。CS命令はこのような主記憶域の更新動作を排他制御付きでやってくれる命令です。最初はわかりにくいかも知れませんが、カウンター更新のサンプルを見てその動きをぜひ覚えて下さい。SETLOCKなどを使うことはまずないと思いますが、CS命令によるフルワード領域の更新はシステムプログラムを書く場合、必須の知識になります。

CS命令では更新に失敗すると(他のタスクで更新された)、第1オペランドレジスターに最新の主記憶内容が読み込まれます。これは大切なポイントです。最初のサンプルと少し変えてラベルTRYAGAINを1つ前の命令にずらしてみました。このようにCTRWORDを読み直すようなロジックを何度か見たことがあります。もちろん間違いではありませんが余計なことです。失敗したCS命令によって読まれた最新の値に+1することで正しくカウンターは更新できます。

CDS命令はCS命令と同じ動作で、対象領域がダブルワードに拡張されたものです。なおLoadやSTore命令と異なり、第2オペランドで指定する主記憶アドレスはワード境界またはダブルワード境界になっていなけれ指定例外でABENDします。


トランザクションのキューイング

トランザクション・キューイング(チェイニング)のサンプルです。QUEUEPTRフィールドは最後に発生したトランザクションを示す制御表のアドレスを持っています。そこからポイントされる制御表(TRXBLOCK)のNEXTTRXフィールドはさらにその1つ前のトランザクションをポイントします。最初に発生したトランザクションのNEXTTRXはそれより古いのがないので0になっています。
上の図に示すように最新のトランザクションをQUEUEPTRに繋げ、そのNEXTTRXフィールドにそれまでの最新であるTRX2のアドレスが入るようにします。こういうのを連結リストなどと言いますが、ここではアルゴリズムのことは解説しません。MVSではOS自身もこのような構造でコントロールブロックを連結しているものがあります。(TCB?RBチェインなど)

サンプルではQUEUEPTRはプログラム内に持っていますが、実戦ではこのフィールドはプログラムの外に置かれ、複数のタスクで参照されます。トランザクションをキューイングする各々のタスクがQUEUEPTRフィールドにTRXBLOCKを繋げようとしますから競合します。CS命令を使えばこのような簡単なロジックで、ロストすることなく確実にチェインさせることができます。取り出すときはFIFOであればQUEUEPTRからリンクをたどり、NEXTTRXが0になっているチェインの最後のTRXBLOCK(TRX1)を取り出します。この時、1つ前のTRXBLOCK(TRX2)にTRX1のNEXTTRXの内容(実際は0)を格納します。取り出し時も、1つしかTRXBLOCKが繋がっていなければQUEUEPTRが、2つ以上繋がっていればTRXBLOCKが競合しますから、やはり更新にはCS命令を使う必要があります。そちらのサンプルは例示しませんので興味がある方はご自分で考えてみてください。


ビットフラグの排他制御付き更新

フラグビットのON/OFFにはOIおよびNI命令を使いますが、マルチタスクで同じフラグバイト内のビットを扱うとやはり競合が発生します。ビットを1にするタスクとビットを0にするタスクが異なる場合、何も考えずにOI命令とNI命令を使うと手痛い目に遭うことがあります。そういうプログラムを作ってしまい、市場に出してしまってから泣きそうな苦労をしていたエンジニアを見たことがあります。ほとんどのケースでは上手く行くが、タイミングによって問題が浮かび上がる、と言う典型的な例でもあります。一番いいのは競合するようなフラグを使わずに済むデザイン、使ったとしてもマルチタスクで突き合うようなことをしないで済むデザインを考えることですが、どうしてもフラグで制御したい方はOI/NI命令の代わりにOIL/NILマクロを利用できます。
上のサンプルはいずれもCNTLFLG2領域にFLAG2ビットをON/OFFする例ですが、OIL/NILマクロを使うと前後のバイトを含めたワード単位でCS命令によって更新が掛かります。作業用に3つのレジスターを必要としますが、WREGSパラメーターで指定することができます。省略時はGR0,1,2の3レジスターが使われます。この例ではレジスター2に代えてレジスター14を使うようにしています。WREGS=(,,14)はWREGS=(0,1,14)と同じです。このマクロはAPF許可プログラム用のAPIマニュアルに記載されていますが、実際はAPF許可は不要です。MSPとVOS3ではマニュアルに記載がありませんが、まったく同じマクロが提供されています。MVS3.8のSYS1.MACLIBにも入っていて30年以上も使われている伝統的なものでもあります。CS命令の応用的な使い方の実例でもありますので、マクロの展開形を見ると勉強になります。

MVSで動くプログラムであれば逐次化の方法には3種類あることを知って下さい。最も汎用的なのがENQ/DEQマクロによる方法でどんなものに対しても排他制御を掛けることができます。ただしオーバーヘッドは最も大きく、頻繁に掛け合う排他制御に使うとCPU消費量は大きくなります。
オーバーヘッドを避けるにはSETLOCKマクロによるロックを使う方法もありますが、本来はMVSがOSとしての制御に使うものでアプリケーションが自分の排他制御に使うものではありません。知識として知っておけばいいでしょう。SETLOCKを必要とするようなプログラムをきちんと作れるようになったらシステムプログラマーとしては上級レベルです。(SETLOCKマクロが使えればと言う意味ではありません…)
CS(CDS)命令によるメモリー領域の逐次化の方法はシステムプログラミングの基本で、メインフレームの仕組みでは最もオーバーヘッドの少ない排他制御になります。メモリー領域の更新であればENQ/DEQサービスを使う前にCSあるいはCDS命令を組み合わせたロジックで排他制御が実現できないかをまず考えましょう。連続しない複数のフィールドを一連の処理として更新する場合は命令による逐次化はできませんが、逆に言えば命令による逐次化が可能なデータ構造にできないかを考えることも重要です。

IBM社のz/Architecture解説書の付録「数の表現と命令の使用例」に「マルチプログラミングとマルチプロセッシングの例」が記載されています。ここに書かれているCS/CDS命令の解説やそれを使った各種のサンプルは、そのまま実戦のプログラミングに流用できる非常に優れたものです。命令の使い方を覚えたらぜひ一度は読むことを勧めます。

Filed in 中級編

03.1資源の逐次化(排他制御)を行う(ENQとDEQ)

By kamii - Last updated: 日曜日, 11月 16, 2008

MVSではあらゆるデータを資源(Resource)として扱うことができます。CPU自身、1つのDASDボリューム、1つのデータセット、データセット内の1つのレコード、実行するプログラム・モジュール、仮想記憶上の領域、などプログラムがアクセスできる装置やデータはすべて資源になります。MVSでは同時に複数のプログラムが動くので、同じ資源を扱うプログラムがその内容を同時に書き換えようとすることが起き得ます。これが資源の競合です。MVSでは競合を解決する手段として、資源を順番に使用させる、逐次化(Serialization)と言う制御を行います。プログラムには逐次化を行うためのENQおよびDEQと呼ばれるサービスを提供しています。
競合する資源には名前を付けて管理します。名前はQ名およびR名で構成されます。一般にQ名が資源(データ)の分類名、R名が資源自身の名前となりプログラムで自由に付けることができます。ただしQ名はMVS自身が制御で使用する名前と重複すると混乱するのでSYSで始まる名前を使うべきではありません。MVSはQ名/R名を実際の資源と関連付けることはしません。存在しない資源の名前を付けることも自由です。ただし同じ資源を使うプログラムはすべて同じQ名/R名を使う必要があります。資源には有効範囲がありスコープと呼ばれます。MVSはQ名、R名、スコープの3つが同じであれば、同一資源とみなしFIFOでその使用を1つのタスクにのみ許可します。ENQは資源の使用許可願い、DEQは資源の使用終了連絡となります。


ENQ/DEQの基本的な使い方

アプリケーションっぽいENQ/DEQの例です。マスターファイル内の口座レコードの残高を更新します。マスターファイルから更新対象レコードを読み込み、残高フィールドを更新して書き戻します。レコードを読み込み?書き戻しまでの間、同じレコードが他のプログラムで書き込まれないように排他制御を掛けます。レコードの整合性を保つため、レコードの読み込み前に排他制御を掛けています。

ENQで排他制御を掛け、DEQでそれを解放します。Q名は8バイト固定で内容は任意です。R名は1から255バイトで内容は任意です。スコープはOS内全体(複数のジョブ間で排他制御を行える)を指定しています。ENQマクロに指定したRET=HAVEは、資源が獲れなければ獲れるまで待ち状態に入ることを示します。ENQ/DEQにRETオプションが指定されていると、ENQ/DEQに失敗すると復帰コードで通知されます。GR15が0ならば要求は成功です。失敗した場合、GR15にはENQ/DEQのパラメーターリストのアドレスが格納されます。復帰コードそのものが格納されるわけではないことに注意して下さい。

ENQ/DEQの特徴はロックする資源は実体を伴わない点にあります。Q名とR名はプログラム間で取り決めたものであって、現実に対応する装置、ボリューム、データセットなどの実体の名前との関連付けなどは行われません。またQ名とR名で指定された資源に対し、物理的に参照や更新などのアクセス自体をできなくすることもありません。あくまでもプログラム間で「○○する時はQ名とR名が書かれた札を取ってから行いましょう、札が使われていたら空くまで待ちましょう。」と言うルールを取り決めているだけです。ENQ/DEQはそのルールで使う札のしくみを提供しているようなものです。Q名とR名の札は、単線の鉄道で使われる通票と同じで、それを持っていないと列車を走らせることができません。
関係するプログラムすべてが同じルールを守ることが前提の排他制御なので、掟破りをする者がいれば成り立ちません。ENQを無視してデータセットに書き込みをすればレコードは壊れてしまいます。トランザクションのキューイングやカウンターの更新であれば、ロストしてしまうかも知れません。MVSは排他制御のしくみを提供するだけで、正しい排他制御がなされるかはあくまでもアプリケーションの設計と運用に掛かってきます。
現在はデータベース・システムを使うので、サンプルで示したようなマスターファイルのレコード更新などはデータベース側の排他制御機能を利用することになります。しかし排他制御が必要な資源はデータベースに格納できるデータレコードだけではありません。ENQ/DEQによる資源の逐次化制御はMVSにおける排他制御の基本ですから、必ず理解しておきましょう。


可変長のR名

このサンプルではR名の長さをENQマクロのパラメーターではなく、R名領域の先頭1バイトに設定することで指定しています。この場合ENQマクロではR名長を0としておきます。この指定方法を使えば可変長のR名長に対応できます。なおR名先頭のレングスフィールドはR名には含まれません。
RET=TESTは指定した資源が誰かに使用されているかをテストする目的で使います。仮に誰も資源を使用していなくても実際にENQされることはありません。スコープはシステム全体(複数のOS間で排他制御を行える)を指定しています。SYSTEMS ENQでは各々のシステムのENQ要求はGRSによって変換・管理され、各々のシステムのGRSは互いのENQ要求をCTCやXCFを介して調整し合います。
サンプルで示したQ名とR名はISPFエディターが編集中のPDSメンバーをロックするためのものです。復帰コードが0でなければそのメンバーは誰かがISPFエディターで編集中ということになります。


複数資源の同時排他制御

複数のリソースを同時にENQすることもできます。サンプルのようにQ名,R名,タイプ,R名長,スコープの対を順番に指定します。RETパラメーターを省略しているので、指定したすべての資源の排他制御が獲れるまでプログラムには戻ってきません。二重ENQなどのプログラムミスがあった場合はABENDさせられます。二重ENQは運用上は問題がなくてもプログラムに誤りがあることには変わりません。ABENDを嫌ってRET=HAVEやRET=USEにすることは多いのですが、その場合は復帰コード8で通知されます。このコードを判定してエラー処理をしなければ二重ENQがあっても潜在化されてしまいます。

アプリケーションなどで1つの処理を行うために複数の資源を持つことはあり得ることです。この場合資源を獲得する順番はとても重要です。AとBの2つの資源がある時、PGM1はA→Bの順番で資源を確保し、PGM2はB→Aの順番で資源を確保しようとします。PGM1がA、PGM2がBの資源を持った状態でお互いが自分が持っていない資源を獲りに行きます。しかしその資源はすでに相手が持ってしまっていますから獲ることができません。このような状態がデッドロックです。これを防ぐ方法として必要な資源を確保する順番を決めると言うのがあります。PGM2もA→Bの順番で資源を確保すれば、Aの確保の時点で実行が止まり、BはENQされないのでデッドロックになりません。もう1つの方法は複数の資源を順番ではなく一度に全部確保することです。ENQ/DEQマクロで複数のリソースが指定できるのは後者の方法をサポートするためです。


データセットの排他制御

RETパラメーターにUSEを指定すると、指定した資源が他のタスクやジョブで使用されている場合、ENQサービス内で待ちになることなく、すぐにプログラムに制御が戻ります。復帰コードが4であれば資源の排他制御は確保されていません。リソースが空いたかどうかは自分で確認のロジックを作成する必要があります。またENQ資源はタスク単位に管理されます。ENQとDEQは同じタスクで発行しなければなりません。

このサンプルはJCLのDD文に定義されたデータセットと同じENQを発行しています。イニシエーターはDD文に定義されたデータセットを排他制御するためにENQを使います。Q名=SYSDSN、R名=データセット名、R名長=実際のデータセット名の長さ、SCOPE=SYSTEMです。DISP=SHRの場合は共用制御のS、SHRでなければ排他制御のEが指定されます。サンプルでは指定したDSNでJCLのDD文と同じENQを発行します。資源が獲得できたら20秒間WAITします。20秒後に起き上がり獲得した資源を解放します。このサンプルを実際に実行した場合、実行後20秒以内に以下のようなDD文を持つJCLをサブミットすれば、「JOB ???????? WAITING FOR DATA SETS」のメッセージを出して、データセットが使用可能になるまで実行開始が延期されるはずです。

SYSで始まるQ名のうち、MVSが制御のために使用するものは一般のプログラムでは使用できません。使用するためには最低でもAPF許可を受けていなければなりません。APF許可されていないプログラムではENQマクロ発行時にABENDS338してしまいます。MVSの各コンポーネントがどのようなQ名、R名でENQを行っているかはマニュアル(診断の解説書)にも載っています。
APF許可を受けたプログラムであれば、すぐに資源が利用できないとき、資源が使用できるようになったらECBに通知してもらったり、異なるタスクの資源をDEQしたりなど、制御プログラム用の機能を利用することができます。

この記事で解説したENQ/DEQに関する機能はMSPもVOS3も共通です。

Filed in .基礎編

02.1日付と時刻を得る(TIMEとTOD時計)

By kamii - Last updated: 日曜日, 11月 16, 2008

現在の日付と時刻を得る

TIMEマクロは現在の日付と時刻を求めます。DECが基本的な返答形式で、GR0に時刻をHHMMSSTH(時分秒)で返します。Tは1/10秒、Hは1/100秒を示します。他にもいくつかの形式がありますので必要に応じて使い分けます。日付はGR1にパック10進数0CYYDDDFで返ります。Cは世紀を表す数字で21世紀(2000?2099年)は1です。2008年なら1900年を起点に108年と見ればよいわけです。DDDは1月1日からの通算日付でジュリアンDATEと呼ばれます。Fはパック10進数の符号です。
日付/時刻を得るための基本的なAPIですが、日付を月日で得られないためプログラム側でYYDDD形式をYYMMDD形式に変換する必要があります。
MSPではDATE=YMD、VOS3ではDTYPE=YMDを指定すれば日付は CYYMMDDF の形式で返されます。


現在の日付と時刻を得る(新形式)

MVSでのみ有効な新形式のTIMEマクロです。現時刻と日付はレジスターではなく指定した16バイトの領域に符号無しパック10進数で返されます。時刻は1/100秒未満も返され、MIJUはそれぞれ1/1000、1/10000、1/100000、1/1000000秒に対応します。
マクロ名は同じTIMEですが、LINKAGEパラメーターで新旧形式が区別されます。


TOD時計

TOD時計(TOD Clock)はハードウェアのタイマー機構の1つです。実際の時刻を示し、すべてのCPUで共用されます。
TOD時計は64ビットの2進数カウンターで、ビット51が1マイクロ秒(1/1000000秒)を示します。1マイクロ秒毎にビット51に1が加算されます。1マイクロ秒未満のビット52?63はハードウェア性能によって分解能が変わるため時刻表示には使われません。しかしどのモデルであってもビット51は正しく1マイクロ秒毎に加算されるように設定されます。またビット31は1.048576秒毎に加算されます。ほぼ1秒です。そのためプログラムによってはTOD時計の先頭ワードのみを参照すれば、現在の日付と時刻を得るのに十分な精度が得られます。


0から51までのすべてのビットが1になるまで約143年掛かります。S/370アーキテクチャーではTODのスタート時刻は1900年1月1日午前0時O分0秒ちょうどになっています。まだ35年も先ですが、2043年にはTOD時計は一周して0に戻ってしまいます。

STCKはCPU命令です。現在のTOD時計の値をダブルワードの領域に格納します。STCKEはSTCKの拡張版で、ESA/390アーキテクチャー以降104ビットに拡張されたTOD時計の値を16バイトの領域に格納します。104ビットに拡張されたTOD時計も先頭のビット0?ビット51までは同じです。ビット51は1マイクロ秒を示します。将来の新CPUモデルでは2043年にTOD時計が一周してビット0が繰り上がると、STCKE命令で格納される先頭バイトに格納されるようになっています。プログラムは引き続き1900年を起点にする標準エポックで計時が可能になります。2043年以降も使われるプログラムを作るならSTCKE命令による拡張されたTOD時計のフォーマットと返答領域について知っておく必要があります。現時点で2043年以降のTOD時計について技術情報が公開されているのはIBM社のzアーキテクチャーだけです。

TOD時計の値はTIMEマクロでも得ることは可能ですが、わざわざAPIコールをする必要はありません。STCK命令で直接求められます。

TOD時計の値はそのままではただの2進数カウンターに過ぎないので、表示に使う場合はYYYYMMDDとHHMMSS形式に変換する必要があります。「OS/390アセンブラーハンドブック」でもTOD時計値の変換方法を解説していますが、現在では自作する必要はなく、クロック値を変換するサービスが用意されています。MVSではSTCKCONV、MSPではTIMECNVTの各マクロ命令で呼び出すことができます。VOS3には同等サービスがありません。MVSのSTCKCONVはSTCKVALに代えSTCKEVALパラメーターを使えば拡張TOD時計値をサポートしますので、2043年以降もプログラム修正の必要がありません。

TIMEマクロを使うかSTCK命令を使うかはプログラムデザイン次第ですが、日付や秒程度の分解能でかまわない時刻であればTIMEマクロが手軽です。デバッグや性能測定のためのログ情報などであれば1/100秒程度ではメインフレームの場合分解能が低すぎるので1/1000000秒まで測定できるTOD時計値が適しています。頻繁に現時刻を取得するならSTCK,STCKEはただの機械命令ですからパフォーマンス面では軍配が上がりますが、将来性を考えてSTCKEを使用すると格納領域長が16バイト必要になります。

Filed in .基礎編

レジスター番号の表記

By kamii - Last updated: 土曜日, 11月 15, 2008

多くのプログラマーはCPU命令のオペランドで指定するレジスター番号を、単に10進数ではなく Rn のようなシンボル名で指定します。これは即値としての数字と、レジスター番号を区別しやすくする工夫の1つです。たいていは頭にRを付けてR0,R1,R2,…,R9,R10,R11,…と書くことが多いです。10以降は16進数で、RA,RB,RC,RD,RE,RFと書くプログラマーもいます。私も後者です。理由は簡単で、0から15まで桁数が変わらないので、レジスター番号を変える場合(例えば LA R1,WORKAREA を、LA R15,WORKAREA に変える)コーディング時に挿入や削除をしなくて済むからです。

汎用レジスターだけを使うプログラムでは Rn あるいは Rx 表記でいいのですが、浮動小数点命令やアクセスレジスターを使うプログラムではレジスター種別をわかりやすくするため、汎用レジスターならGRnあるいはGPRn、浮動小数点レジスターはFPRn、アクセスレジスターはARnとしたりします。制御レジスターならCTLRnです。
またレジスターの使用目的でシンボル名を付けることもあります。GR13はレジスター保管域アドレスをポイントするのでRSAVEA、GR12をベースアドレスにするならRBASEのようにです。

レジスター番号は数字での直接指定、シンボル名での指定、どちらも自由です。単なる番号ではなく用途に合わせたシンボル名でもかまいません。ただし開発プロジェクトなどでレジスター表記の規約が定められているような場合はそれに従います。

Filed in S/370アセンブラー講座

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

By kamii - Last updated: 金曜日, 11月 14, 2008

プログラムで使うデータ領域には、あらかじめその大きさを固定できないものもあります。例えばデータセットを読み込む処理で、レコードを読み込む領域の大きさを実際のデータセットのレコード長に合わせるような場合です。考え得る最大の長さでプログラム内に領域を用意するのも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マクロによる方法はコーディングも容易で、どのようなモードのプログラムでも共通に利用できるストレージサービスです。


ワンポイント知識

Filed in .基礎編