QSAM

By 神居 - Posted: 2009/10/22 Last updated: 2009/10/23 - Leave a Comment

QSAM:Queued Sequential Access Method,待機順アクセス法

QSAMの特長

QSAMは順次データセットを、レコード(論理レコード)単位にアクセスするためのプログラミング・インタフェースです。特長はレコード単位であるため、プログラミングが容易であること、自動的なバッファリング制御により、高速なI/O処理が行われることです。
QSAMの他に、BSAM(Basic Sequential Access Method,基本順アクセス法)があります。こちらはレコード単位ではなく、ブロック単位のアクセスとなり、自動バッファリングは行われません。バッファリングの制御はプログラムが自分で行う必要があります。BSAMは途中のブロックを読み飛ばしたり、後ろへ戻ったり、と言った任意の位置からのI/Oを行ったり、I/O処理と他の処理をオーバーラップさせる、非同期I/O制御を行うなど、きめ細かなI/O制御ができますが、それらは一般のデータセットのアクセスではほとんど必要とされません。

QSAMはビギナー向けのEasyなI/O、と言うイメージがありますが、内部では効率のよいバッファリング制御がなされ、高速なI/O処理が可能です。READ処理の場合、データセットのOPENと同時に、5ブロック分が先読みされます。アプリケーションがGETマクロでレコードの読み込みを要求すると、バッファ内のブロックをデブロッキングして、論理レコードをプログラムに渡す(移動モード)か、レコードの先頭アドレスを通知します(位置づけモード)。QSAMは論理レコード単位でアクセスできるため、ブロッキングとデブロッキングと言う、本来のアプリケーションには必要のない処理を作る必要がなく、プログラムはその分、ビジネスロジックに専念できます。そして複雑なバッファリング制御を自ら行わなくとも、高速なI/O処理が行え、しかもI/O処理に使用するバッファの数は、JCLでも指定できるので、データ量に応じてバッファ数を容易に調整できます(省略時は5ブロック分)。現在では特別な理由がない限り、BSAMを選択する必要はないでしょう。COBOLなどのコンパイラー言語でも、順次データセットのアクセスにはQSAMが利用されています。


QSAMの機能

QSAMには次のI/O機能が提供されています。(抜粋)


バッファプール

QSAMはバッファを使用して、複数ブロックをまとめてI/Oするので、効率がよいアクセス法です。バッファ数はプログラムまたはJCLで変更できます。プログラムで指定する場合は、DCBマクロにBUFNOパラメーターを指定します。JCLで指定する場合は、DD文にDCB=BUFNOパラメーターを指定します。どちらにも指定されない場合は、5が内部で採用されます。なおプログラムでDCBに指定してしまうと、JCLのDD文に指定しても有効にならないので、特別な事情がない限り、DCBマクロでは指定しない方がよいと考えます。
実際どの程度のバッファ数が、パフォーマンスに効果があるかは一概には言えないのですが、昔、テストプログラムで実測したことがあって、その結果では、バッファ数は5?10ぐらいまでが、バッファ数1に比べて、メモリー量も程よく、CPU使用量も減り、ELAPも短縮される、となっていました。それ以上は、例えば100個用意しても、CPU量もELAPもさほどの減少効果はなく、メモリー量だけはバッファ数分しっかり増える、となっていました。つまりメモリー量に見合うほど、CPUとELAPは減らず(メモリーが100倍でもCPUが1/100になるわけではない)、あるところで頭打ちになるわけです。OSのデフォルトが5なのを考えても、妥当な数だと考えます。テストしたデータセットのブロック長は、23476バイトでしたが、ブロック長が比較的小さなデータセット(数千バイト以下)の場合は、もう少し増やしても(20?30ぐらいまで)いいかも知れません。
なお、MSPではQSAMのI/Oバッファは16MBの上の拡張域に作成できないので、ブロック長の大きなデータセットではむやみにバッファ数を増やしても、リージョンが圧迫されるだけです。MVSとVOS3であってもQSAMバッファを31ビット領域に作成するかどうかは、プログラム次第です。

プログラムが使用したバッファプールの解放は、基本的にQSAMルーチンではなく、QSAMを使用したアプリケーション・プログラムの責任でした。従来はCLOSEマクロの後に、同じDCBを再使用して、そのデータセットを再OPENしないのであれば、FREEPOOLマクロによってバッファを明示的に解放する必要がありました。オンライン処理(対話処理)などで、処理するデータセットを端末から入力させるような場合、処理の都度DCBをGETMAINし直し、OPENに使うようなプログラムでは、プログラムでの処理が繰り返されていくうちに、やがてリージョン不足になってしまうことになってしまいます。CLOSEはきちんと出すがFREEPOOLはしていない、と言うプログラマーも結構いるようです。バッチ処理などではあまり問題になりませんが、使った資源は解放するのが基本なので、CLOSEとFREEPOOLはペアーで覚えることを奨めます。
しかしFREEPOOLは不要な場合もあります。それはMVSでDCBEを使いQSAMのバッファを31ビット領域に確保させる場合です。QSAMが使ったバッファはCLOSEルーチンの中で解放されるため、FREEPOOLは必要ありません。また富士通のXSPもFREEPOOLはありません。MSPとXSPの両方をサポートするプログラムの場合は、QSAMそのもののマクロや使い方は似ていますが(DCBがFCBとなるが、基本は同じ)細かな点で相違点があることに注意します。日立のVOS3ではQSAMバッファをMVSと同じく31ビット領域に確保できますが、解放にはMVSと違ってFREEPOOLマクロが必要です。


書き込みデータの保証

QSAMは使いやすく、性能もよいアクセスメソッドですが、x37ABENDのリカバリーが難しい点に注意します。プログラムをRE-RUNすることで、データをリカバリーできるなら問題ありませんが、PUTした後、元のレコードが消えてしまい、RE-RUNで再作成もできない場合は、x37ABENDしないだけの十分な大きさを確保するか、BSAMを使うか、データセットをVSAMに変更できないかの検討も必要です。
QSAMではPUT要求の処理と実際のデータセットへの書き出し処理は連動しません。PUTの完了とはバッファ内にレコードデータが書き込まれるだけです。バッファが一杯になった後、次のPUTの時にI/Oが行われます。それは最初のPUTよりもはるかに後になってからかも知れません。x37ABEND(他のI/Oエラーも含め)は実際のI/Oが行われないと起きませんので、起きたときにはアプリケーションからはレコードデータが消えています。また必ずしもPUTで起きるわけでもありません。最終ブロックの書き込みであれば、CLOSEの延長で発生します。ABEND事象そのものはDCB ABEND出口やESTAEでトラップできますが、リカバリーはかなり面倒です。

具体的にどの程度の量のレコードがロストするかは、ブロッキングファクター×BUFNO数で求められます。例えば、RECFM=FB、BLKSIZE=800、LRECL=80なら、1ブロックに10レコード入るので、BUFNOが5なら50レコードがロストします。BUFNOが1なら10レコード、10なら100レコードのロストです。固定長レコードの場合は、BUFNO分のブロックデータをメモリーなどにSAVEすればリカバることはできるでしょう。BUFNOの実際の値はOPEN後のDCBを見ればわかります。しかし可変長レコードの場合、ブロッキングファクターは個々の論理レコード長に左右されブロック毎に変わります。正確なリカバリーを行うのは無理ではないにしてもかなり面倒です。
私はパフォーマンス上の理由があって、どうしてもQSAMを採用する必要があり、x37ABENDのリカバリー用にPUTしたレコードのコピーをデータ空間に持つ方法を使ったことがあります。その前に、QSAMバッファには書き出せなかったブロックが残っているから、再OPENした後にそれをTRUNCマクロでRE-WRITEできないかなど、いろいろ試したこともありました。メモリーだけ見るとレコードのデータが残っているし何とかできないかって。バッファも予めGETPOOLしたりも試したけど、結局DCB ABEND出口から戻った時には、プール内のバッファは返却されてしまっていた。そのためバッファの先頭に置かれているレコードの頭はバッファチェインのアドレスで壊されてる箇所もあって、上手く行かなかった記録が残ってます。何かやり方がないかあがいたのですが、結局見つけられませんでした。
いずれにしても、x37ABENDをABEND出口でトラップし、無視を要求して、PUTの直後から再開させても、そのPUTで書き込もうとしたレコードより前のレコードは、全部がデータセットに書き出されているわけではありません。復元できない重要なデータを、順次データセットや区分データセットのメンバーとして書き込む場合、このような特性は知っておくべきことです。


サンプルコード

いずれのサンプルも、データセットのコピーを行うQSAMプログラムです。MVS、MSP、VOS3に共通な24ビットモードのQSAMで、マクロの使用に関しては同じです。ただしMVSのHLASM前提のコーディングなので、MSPとVOS3に関しては必要に応じて修正してください。SYSUT1からSYSUT2のDCBにRECFM、BLKSZ、LRECLをコピーするためのMVC命令の箇所です。
なお31ビットモードのQSAMプログラミングについてはこちらのページ「DFP・31ビットモード・プロセッシング」でも解説しています。

UT1      USING IHADCB,SYSUT1            ADDRESS FOR SYSUT1 DCB
UT2      USING IHADCB,SYSUT2            ADDRESS FOR SYSUT2 DCB
         OPEN  (SYSUT1,INPUT)           OPEN INPUT DATASET(ORIGIN)
         MVC   UT2.DCBRECFM,UT1.DCBRECFM  COPY RECFM TO SYSUT2
         MVC   UT2.DCBBLKSI,UT1.DCBBLKSI  COPY BLKSZ TO SYSUT2
         MVC   UT2.DCBLRECL,UT1.DCBLRECL  COPY LRECL TO SYSUT2
         OPEN  (SYSUT2,OUTPUT)          OPEN OUTPUT DATASET(DEST)
LOOP     DS    0H
         GET   SYSUT1                   GET NEXT RECORD
         LR    R0,R1                    LOAD LOGOCAL RECORD ADDRESS
         PUT   SYSUT2,(0)               PUT NEXT RECORD
         B     LOOP                     LOOP FOR NEXT RECORD
EODAD    DS    0H
         CLOSE (SYSUT1,,SYSUT2)         CLOSE USED DATASET
         FREEPOOL SYSUT1                FREE QSAM I/O BUFFER
         FREEPOOL SYSUT2                FREE QSAM I/O BUFFER
         B     EXITPROC                 PROCESSING DONE
         SPACE ,
SYSUT1   DCB   DDNAME=SYSUT1,MACRF=GL,DSORG=PS,EODAD=EODAD
SYSUT2   DCB   DDNAME=SYSUT2,MACRF=PM,DSORG=PS
UT1      USING IHADCB,SYSUT1            ADDRESS FOR SYSUT1 DCB
UT2      USING IHADCB,SYSUT2            ADDRESS FOR SYSUT2 DCB
         OPEN  (SYSUT1,INPUT)           OPEN INPUT DATASET(ORIGIN)
         MVC   UT2.DCBRECFM,UT1.DCBRECFM  COPY RECFM TO SYSUT2
         MVC   UT2.DCBLRECL,UT1.DCBLRECL  COPY LRECL TO SYSUT2
BLKSIZEのコピーはしない。RECFMとLRECLは変えないがBLKSIZEはコピー先
データセットの装置タイプに合わせて最適化される。(MVSのみ)
         OPEN  (SYSUT2,OUTPUT)          OPEN OUTPUT DATASET(DEST)
LOOP     DS    0H
         GET   SYSUT1                   GET NEXT RECORD
         LTR   R0,R1                    EOF ?
         BZ    EODPROC                  YES, DO CLOSE DATASET
         PUT   SYSUT2,(0)               PUT NEXT RECORD
         B     LOOP                     LOOP FOR NEXT RECORD
EODPROC  DS    0H
         CLOSE (SYSUT1,,SYSUT2)         CLOSE USED DATASET
         FREEPOOL SYSUT1                FREE QSAM I/O BUFFER
         FREEPOOL SYSUT2                FREE QSAM I/O BUFFER
         B     EXITPROC                 PROCESSING DONE
         SPACE ,
EODAD    DS    0H
         SLR   R1,R1                    INDICATE DATASET IS EOD
         BR    R14                      RETURN TO MAIN LINE
GLモードならEODADルーチンはこのように汎用化できる。
複数のDCBを使うプログラムなどでは便利な方法。
GR14の示すアドレスは、GETマクロの次のアドレスを指している。
         SPACE ,
SYSUT1   DCB   DDNAME=SYSUT1,MACRF=GL,DSORG=PS,EODAD=EODAD
SYSUT2   DCB   DDNAME=SYSUT2,MACRF=PM,DSORG=PS
UT1      USING IHADCB,SYSUT1            ADDRESS FOR SYSUT1 DCB
UT2      USING IHADCB,SYSUT2            ADDRESS FOR SYSUT2 DCB
         OPEN  (SYSUT1,INPUT)           OPEN INPUT DATASET(ORIGIN)
         MVC   UT2.DCBRECFM,UT1.DCBRECFM  COPY RECFM TO SYSUT2
         MVC   UT2.DCBBLKSI,UT1.DCBBLKSI  COPY BLKSZ TO SYSUT2
         MVC   UT2.DCBLRECL,UT1.DCBLRECL  COPY LRECL TO SYSUT2
         OPEN  (SYSUT2,OUTPUT)          OPEN OUTPUT DATASET(DEST)
LOOP     DS    0H
         GET   SYSUT1                   GET NEXT RECORD
         PUTX  SYSUT2,SYSUT1            PUT NEXT RECORD
PUTではなくPUTXの出力モードを使用した例。書き込むレコードアドレスは
コピー元のDCBから得られるようになっている。
         B     LOOP                     LOOP FOR NEXT RECORD
EODAD    DS    0H
         CLOSE (SYSUT1,,SYSUT2)         CLOSE USED DATASET
         FREEPOOL SYSUT1                FREE QSAM I/O BUFFER
         FREEPOOL SYSUT2                FREE QSAM I/O BUFFER
         B     EXITPROC                 PROCESSING DONE
         SPACE ,
SYSUT1   DCB   DDNAME=SYSUT1,MACRF=GL,DSORG=PS,EODAD=EODAD
SYSUT2   DCB   DDNAME=SYSUT2,MACRF=PM,DSORG=PS
Posted in キーワードから(何が知りたいですか) • • Top Of Page