皆さん、こんにちは!
メインフレームの世界へようこそ。世界最高峰の基幹システムを支えるPL/I、そしてオンライン制御プログラムであるCICS(シーアイシーエス)の世界は、JavaやCOBOLとはちょっと違う独特の空気が漂っていて、最初は少し怖く感じるかもしれませんよね。
「なんだか変数名のルールが変だし、メモリの管理も自分でやれって言われた……」なんて、冷や汗をかいている方もいらっしゃるのではないでしょうか?
でも、安心してください。ベースにある考え方は他の言語と一緒ですし、一つずつ紐解いていけば決して難しくありません。今回は、CICS環境のPL/Iプログラムにおける「動的メモリ管理(GETMAIN/FREEMAIN)とストレージリークの恐怖、そしてその対策」について、現場のノウハウをたっぷり交えて優しく解説していきますね!
—
1. JavaやCOBOLとはここが違う!PL/Iのちょっと変わった識別子と「予約語がない」世界
まず、CICSのコードを書く前に、PL/Iという言語のユニークな性格を少しだけ知っておきましょう。
JavaやC言語を触ったことがある方なら、「このキーワードは予約されているから変数名に使えないんだな」というお約束をご存知だと思います。例えば、`IF`や`READ`といった命令語をそのまま変数名にするなんて、普通は怒られますよね。
ところが、PL/Iには「厳密な意味での予約語」が存在しません!
どういうことかと言うと、PL/Iは文脈によって言葉の意味を判断してくれる、とっても器用な言語なんです。例えば、以下のようなコードを見てみてください。
1
/ IFという名前の変数を作っちゃう驚きの世界 /
DCL IF FIXED BIN(31); / IFという名前の変数(31ビットの整数)を宣言 /
IF = 10; / 変数IFに10を代入 /
「えっ、条件分岐の `IF` と名前が被っちゃうじゃん!」って思いますよね。でも、コンパイラは前後の文脈(「あ、ここは代入文だから変数の `IF` だな」など)をちゃんと理解できるので、怒られません。
……とはいえ、現場の先輩たちから「だからといって `IF` や `READ` を変数名にするな!」と怒られるのは目に見えていますので、実務では常識的な名前をつけましょうね(笑)。変数名(識別子)は英字で始まり、英数字とアンダースコア(`_`)を使って31文字以内で自由に命名できます。大文字・小文字は区別されない(すべて大文字として扱われる)のがメインフレームのレガシーらしいところです。
—
2. CICSにおける動的メモリ確保(EXEC CICS GETMAIN)の基本
さて、本題のCICSでのメモリ管理です。
オンライン画面から飛んできた大量のデータを処理するとき、「あらかじめどれくらいのメモリ(ストレージ)が必要か分からない!」という場面に出くわします。そんなとき、OSやCICSに対して「今すぐこれだけのサイズの作業用メモリをちょーだい!」とお願いするのが、`EXEC CICS GETMAIN` です。
Javaなら `new` する感覚、C言語なら `malloc` する感覚ですね。
では、実際のPL/Iコードを見てみましょう。CICSのコマンドは、PL/Iの中でもそのまま記述できます。
1
/————————————————————/
/ CICS動動的メモリ確保(GETMAIN)のサンプルコード /
/————————————————————/
DCL WS-REQ-LEN FIXED BIN(31) INIT(1024); / 欲しいサイズ(1024バイト) /
DCL WS-DATA-PTR POINTER; / 割り当てられたメモリの住所(ポインタ) /
DCL 1 WS-WORK-AREA BASED(WS-DATA-PTR), / 住所(ポインタ)をベースにした構造体 /
5 WS-CUST-ID CHAR(8), / 顧客ID /
5 WS-CUST-NAME CHAR(40); / 顧客名 /
/ メモリを動的にゲットする! /
EXEC CICS GETMAIN
SET(WS-DATA-PTR)
LENGTH(WS-REQ-LEN)
INITIMG(X’00’) ; / ついでにヌル文字で初期化しちゃう親切設計 /
/ ここでゲットしたメモリ(WS-WORK-AREA)をごにょごにょ使う /
WS-CUST-ID = ‘12345678’;
WS-CUST-NAME = ‘山田 太郎’;
/ (処理が終わったら必ず解放する!) /
ここで注目してほしいのが、`BASED(WS-DATA-PTR)` という見慣れない記述です。
これはPL/I独特の強力な機能で、「`WS-DATA-PTR` という変数に入っているメモリーの番地(アドレス)を土台(ベース)にして、この構造体のレイアウトをそこに重ね合わせるよ!」という意味になります。C言語のポインタキャストと構造体マッピングに近いイメージですね。
—
3. 悪夢の始まり…ストレージリークの発生メカニズム
メモリを借りたらどうなるでしょう? そう、「返却(FREEMAIN)」をしなければなりません。
もし、返却を忘れたり、エラー発生時にジャンプしてスルーしてしまったりするとどうなるでしょうか?
CICSは24時間365日止まらないオンラインシステムです。タスク(トランザクション)が実行されるたびにメモリがモリモリと消費され、やがてCICSの領域がいっぱいになってしまいます。
これが、エンジニアたちの眠れぬ夜を引き起こす「ストレージリーク(メモリリーク)」の正体です!
1
/ 恐ろしいリークの例 /
EXEC CICS GETMAIN SET(WS-DATA-PTR) LENGTH(WS-REQ-LEN);
/ もし途中で異常系エラーが発生して、そのままリターンしちゃったら… /
IF ERROR-FLG = ‘1’ THEN DO;
/ FREEMAINを呼ばずに終わる! -> 借りたままパァ! /
RETURN;
END;
/ 正常終了時のみ解放しているパターン /
EXEC CICS FREEMAIN DATAPTR(WS-DATA-PTR);
この「解放漏れ」が蓄積していくと、CICS領域全体が枯渇し、最終的にシステム全体がダウン(SOSAB / System Out of Storage)という最悪の事態を招きます。「たった1KBだから……」なんて油断が大敵。オンラインのアクセス数が1日に何百万件もある基幹システムでは、一瞬でサーバーが窒息してしまいます。
—
4. ストレージリークを防ぐ&見つけるためのデバッグ手法
では、もしストレージリークの疑いが出てしまったら、どうやって調査すればよいのでしょうか? ベテランのアーキテクトたちが現場で使っている実践的なアプローチをご紹介します。
① CEDF(CICS Execution Diagnostic Facility)を使ったリアルタイム監視
CICSには強力なデバッグツール「CEDF」があります。トランザクションの実行中に `CEDF ON` と入力すると、プログラムの1ステップごとの動きや、`GETMAIN` で確保されたストレージの数(Task-subpool Storage)をリアルタイムで追跡できます。
「あれ? さっきより確保しているストレージが増えて減っていないぞ?」という異変にその場で気づくことができます。
② ダンプ解析(CECI / IPCS)
万が一、ストレージ枯渇でCICSが異常終了(ABEND: APS0 など)してしまった場合は、システムダンプが採取されます。
メインフレームの解析ツール(IPCSなど)を使い、タスクが保持し続けているストレージのチェイン(鎖)を辿っていきます。PL/Iのランタイムが管理しているヒープ領域のヘッダー情報を読み解き、「どのプログラムがどの瞬間に確保したメモリか」を特定していくわけです。
③ コーディングの鉄則:ON ERROR / ON CONDITION の活用
PL/Iには、例外処理をエレガントに記述できる `ON` ユニットという仕組みがあります。これを使って、プログラム内で予期せぬエラーやABENDが発生した際でも、確実に `FREEMAIN` が通るようなクリーンアップ処理(ファイナライザ的な動き)を組み込んでおくのがプロの技です。
1
/ エラー時でも確実に通るように設計する /
ON ERROR BEGIN;
/ 万が一のエラー時、ポインタが有効なら解放を試みる /
IF WS-DATA-PTR ¬= NULL() THEN DO;
EXEC CICS FREEMAIN DATAPTR(WS-DATA-PTR);
END;
/ その後、システムへ処理を戻す /
GOTO NORMAL-EXIT;
END;
—
おわりに
いかがでしたでしょうか?
「PL/Iの予約語がない自由な構文」や「CICSの `GETMAIN/FREEMAIN` によるメモリ管理」は、一見するとレガシーで難解に思えるかもしれません。しかし、その裏側にある仕組みを一つずつ理解していけば、ハードウェアの動きをダイレクトにコントロールできる、非常にエキサイティングで強力な技術であることが分かります。
「メモリを借りたら、責任を持って返す」
これは現実世界のお金やモノの貸し借りと同じですね。PL/Iという相棒と一緒に、安全で堅牢なメインフレーム・アプリケーションを作っていきましょう!
それでは、次回のレガシー探訪もお楽しみに!
