【実務・中級編】BASED属性とポインタ変数によるメモリ直接操作 – PL/Iの基本構文とデータ制御実践ガイド

おい、最近プロジェクトに入ってきた若手が、オンラインのVSAM制御ブロックを直叩きしようとして地雷を踏みかけたんだよ。「ポインタを使えばC言語みたいに自由なメモリ操作ができてクールですね!」なんて満面の笑みで言ってきたもんだから、思わずコーヒーを吹き出しそうになったね。

甘い。甘すぎるぞ。ここはC言語のサンドボックスじゃない。何百万件もの勘定系トランザクションを毎晩さばく、IBMメインフレームのPL/Iの世界だ。

PL/Iにおける `BASED` 属性とポインタ操作は、正しく使えばマシンの限界を引き出す最強の武器になるが、一歩間違えば夜間バッチを沈没させ、S0C4(アアクセス例外)やOC1といった懐かしい(そして恐ろしい)システム異常終了の嵐を巻き起こす諸刃の剣だ。

今日は、現場で生き抜くために絶対に知っておくべき、`ADDR` 関数、`ADR` 関数、そして見落とされがちな「アライメント違反」の罠について、徹底的に叩き込んでやる。心して聞け。

1. PL/IのポインタとBASED属性の基本思想

まず大前提として、PL/IにはC言語のような「型付きポインタの演算(`ptr++` など)」はない。PL/Iのポインタ(`POINTER` データ型)は、単なる31ビット(または64ビット)のメモリアドレスの入れ物に過ぎない。

そこに `BASED(P)` 属性を付与した構造体を紐付けることで、初めてそのアドレス空間に意味のある「レコード」や「マッピング」が浮かび上がる。

1
DCL 1 W_KOKYAKU_REC BASED(P_KOKYAKU),
5 KOKYAKU_ID CHAR(8),
5 KOKYAKU_NAME CHAR(30),
5 KOKYAKU_AMT FIXED BIN(31,0);

このコードでは、`P_KOKYAKU` というポインタ変数にアドレスを設定するまで、`W_KOKYAKU_REC` の実体はメモリ上に存在しない。アドレスを割り当てて初めて、その場所にあるメモリをこの構造体のかたちで読み書きできるようになる。これが `BASED` の本質なのだ。

2. ADDR関数とADR関数の決定的な違い

メモリを直接操作する上で、避けて通れないのがアドレスを取得・計算する2つの組み込み関数(BUILTIN)だ。ここを混同しているプログラマが多すぎる。

`ADDR` 関数(アドレス取得)

変数や構造体の「論理的なメモリアドレス(POINTER型)」を返す。型安全であり、ポインタ変数にそのまま代入できる。

1
P_KOKYAKU = ADDR(W_WORK_AREA);

`ADR` 関数(数値アドレス計算)

変数のアドレスを `FIXED BIN(31,0)` または `FIXED BIN(64,0)` の「数値(整数)」として返す。
「なぜ数値を返す必要があるのか?」というと、ポインタに対して直接算術演算(足し算や引き算)ができないPL/Iにおいて、オフセット計算を行いたい場合に使う。

1
/ ワークエリアの先頭から64バイト後ろを指すポインタを作る /
P_NEXT = ADDR(W_BASE_AREA); / いったんポインタを取得 /
/ ADRを使って数値としてオフセットを加算し、再びポインタに戻す /
P_NEXT = P_NEXT + 64; / ※PL/Iのコンパイラやオプションによってはポインタへの加算が許容される場合もあるが、厳密なオフセット計算にはADRが使われる /

ただし、やみくもに `ADR` でアドレスをいじくり回すのは、保守性をドブに捨てるようなものだ。「どうしても可変長レコードのヘッダを読み飛ばして実体にアクセスしたい」といった、極限のパフォーマンスが求められるVSAMルーチン以外では封印するのが賢明だな。

3. 実践:VSAM入出力とBASED構造体によるゼロコピー処理

大規模バッチでVSAM(KSDSなど)からレコードを読み込む際、毎回ワークエリアへ `MOVE`(値の転送)を行っているようでは、CPUの無駄遣いだ。`BASED` 属性を使えば、バッファ内のアドレスを直接指すことで、データ転送のオーバーヘッドをゼロにできる(いわゆるゼロコピー)。

以下の実用的なサンプルコードを見てほしい。大文字で記述された、現場の標準的なスタイルだ。

1
—————————————————————-

  • 顧客マスターVSAMファイルからレコードを読み込み、
  • BASED構造体で直接参照・更新するサンプル

—————————————————————-
DEMO_VSAM_PROC: PROC OPTIONS(MAIN);

/ 組み込み関数の明示的宣言 /
DCL ADDR BUILTIN;
DCL NULL BUILTIN;

————————————————————

  • ファイル定義

————————————————————
DCL KOKYAKU_FL FILE RECORD SEQUENTIAL INPUT
ENVIRONMENT(VSAM);

————————————————————

  • 処理変数・ポインタ定義

————————————————————
DCL P_BUF POINTER INIT(NULL);
DCL EOF_FLG CHAR(1) INIT(‘OFF’);

————————————————————

  • BASED構造体(VSAMレコードの物理レイアウトに完全一致させる)

————————————————————
DCL 1 VSAM_REC BASED(P_BUF),
3 REC_ID CHAR(8),
3 REC_STATUS CHAR(2),
3 REC_DATA CHAR(90);

  • 終了条件のONユニット定義 /

ON ENDFILE(KOKYAKU_FL) EOF_FLG = ‘ON’;

  • ファイルオープン /

OPEN FILE(KOKYAKU_FL);

  • メインループ /

DO WHILE(EOF_FLG = ‘OFF’);

  • READ…SET構文により、VSAMバッファ内のアドレスをP_BUFに直接取得
  • これにより無駄なメモリコピーが発生しない

READ FILE(KOKYAKU_FL) SET(P_BUF);

IF EOF_FLG = ‘ON’ THEN LEAVE;

  • BASED構造体を通してフィールドを直接参照・評価

IF REC_STATUS = ’01’ THEN DO;
PUT SKIP EDIT(‘ACTIVE CUSTOMER ID: ‘, REC_ID)
(A, A);

  • 必要に応じてフィールドの値を直接書き換えることも可能
  • REC_STATUS = ’02’;

END;

END;

  • ファイルクローズ /

CLOSE FILE(KOKYAKU_FL);

RETURN;
END DEMO_VSAM_PROC;

この `READ … SET(P_BUF)` と `BASED` の組み合わせは、メインフレームのバッチ処理において高速化を図るための常套手段だ。メモリコピーが走らないため、数百万件のレコードを処理する際には劇的な効果を生む。

4. 恐怖のアライメント違反と性能低下リスク

さて、ここからが今日の最も重要な教訓だ。

「ポインタで自由なアドレスを指せる」からといって、奇数番地や、データ型が要求する境界(境界整列=アライメント)を無視したアドレスを `BASED` 変数に与えるとどうなるか?

  • ハードウェア例外(S0C4など):

System/390やz/Architectureでは、例えば `FIXED BIN(31,0)`(4バイト整数)や `FLOAT` 型は、4の倍数や8の倍数のメモリアドレス(境界)に配置されている必要がある。奇数番地などを指した状態で数値演算を行おうとすると、ハードウェアレベルで例外が発生し、ジョブが即座にアボートする。

  • ハードウェアによるサイレントペナルティ(性能劣化):

アーキテクチャによっては、境界不一致のデータを読み書きする際、CPUが内部で複数回のメモリアクセスとシフト演算を自動的に補正して実行するものがある。プログラムは正常終了するが、「なぜかバッチの処理時間が通常の何倍にも跳ね上がる」という、原因究明が極めて困難な性能劣化病を引き起こす。

対策:ALIGNED属性の活用

構造体を定義する際は、コンパイラに対して適切な境界調整を指示する `ALIGNED` 属性(あるいはデフォルトの挙動)を意識しなければならない。特にCICSの通信エリア(DFHCOMMAREA)や外部プログラムとの間で生データをやり取りする際は、`UNALIGNED`(パック十進数や文字データの詰め込み)と `ALIGNED` の混在に細心の注意を払え。

1
/ 正しいアライメントを意識した構造体定義 /
DCL 1 GOOD_REC ALIGNED,
3 F1 CHAR(1),

  • ギャップ(パディング)が自動挿入され、次変数が4バイト境界に調整される

3 F2 FIXED BIN(31,0);

もし自前でポインタを計算してメモリを切り出すなら、必ずデータ型が要求する境界(2バイト、4バイト、8バイトの倍数)にアドレスが収まるよう、マスク処理やアライメント調整のコードを挟むことだ。

シニアからのメッセージ

PL/Iの `BASED` 属性とポインタ操作は、メインフレームの歴史とハードウェアの構造に直結している。単に「動けばいいや」で適当なアドレスを突っ込んでいると、本番環境のピーク時に突発的なシステムダウンや、説明のつかないバッチ遅延という悪夢を引き起こすことになる。

言語仕様の裏にある「ハードウェアがどうメモリを読んでいるか」を想像しながらコードを書く。それこそが、時代遅れと言われようとも基幹システムを支え続ける、我々メインフレームエンジニアの矜持というものだ。

次の改修では、ポインタの扱いに一層の緊張感を持って臨むように。期待しているぞ。

タイトルとURLをコピーしました