迷宮のGLOBAL変数:PL/I `EXTERNAL` 属性とメモリの深淵
メインフレームの現場で長く生きていると、「なぜこんな仕様なのか」と頭を抱えるコードに遭遇する。特にPL/Iの `EXTERNAL` 属性は、現代の言語設計から見れば「カプセル化の敵」そのものだが、レガシーシステムの心臓部では、今なおこの荒々しい名前解決が基幹バッチの血流を制御している。
今日は、JavaやC#のモダンな設計思想で育ったエンジニアが、マイグレーションの現場で必ず直面する「PL/Iの変数共有」の深淵について、アーキテクトの視点から紐解いていこう。
—
1. 予約語なき設計:自由と責任の境界線
PL/Iが特異なのは、`IF`や`THEN`さえも文脈によって変数名として許容されるという、極めて柔軟(かつ無謀な)言語仕様だ。この「予約語を持たない」という設計は、初期のコンパイラ開発者たちの狂気じみた情熱を感じさせるが、大規模な移行においては脆弱性そのものになる。
特に `EXTERNAL` 属性を用いた変数共有は、リンケージエディタ(リンクエディット)の名前解決に依存する。モジュールAで宣言した `DCL G_WORK_AREA CHAR(1024) EXTERNAL;` は、モジュールBでも同じ宣言があれば、物理的に同一のアドレスを指すことになる。
/i
/ モジュールA: グローバルな共通領域を定義 /
DCL COMMON_BUFFER CHAR(256) EXTERNAL STATIC;
/ モジュールB: 同じ名前と型で受ける /
DCL COMMON_BUFFER CHAR(256) EXTERNAL STATIC;
ここで注意が必要なのは、「コンパイラは宣言の整合性を完璧には検証しない」という点だ。モジュールAが `CHAR(256)` と宣言し、Bが `CHAR(512)` と宣言しても、コンパイルエラーにはならない。結果、実行時に隣接するメモリ領域を破壊(オーバーレイ)し、原因不明の `S0C4` アベンドを引き起こす。これが、我々が「レガシーの神」と呼ぶ悪夢の始まりである。
—
2. ポインタを用いた「禁断の操作」とアベンドの解析
基幹バッチで効率を追求するあまり、`EXTERNAL` な領域を `BASED` 変数とポインタで制御する設計を見ることがある。
/i
DCL P_BUF PTR;
DCL T_DATA CHAR(100) BASED(P_BUF);
/ 共通領域のポインタを取得して動的にマップする /
P_BUF = ADDR(COMMON_BUFFER);
T_DATA = ‘TARGET_VALUE’; / 共通領域を直接書き換える /
この手法は、動的な構造体マッピングには極めて強力だが、ダンプ解析の難易度を跳ね上げる。もしこのポインタ `P_BUF` が不正なアドレスを指していれば、`S0C4`(保護例外)が発生する。
ダンプ解析の鉄則:
1. 制御ブロックの検証: DUMP中のポインタ値が、LOADモジュール内の期待されるアドレス範囲にあるか確認せよ。
2. 符号反転の罠: パックデシマル(`FIXED DEC`)を扱う際、意図せず符号(Zone/PackedのNibble)を破壊すると、計算結果が正負反転するだけでなく、後続のDB2処理で `SQLCODE -180/181` を誘発する。ダンプ上の16進数表記を読み解く際、`0C`(プラス)と `0D`(マイナス)の境界は常に疑え。
—
3. マイグレーションに向けたアーキテクチャ設計
JavaやC#への移行を検討する際、この `EXTERNAL` 変数をどう処理するかがプロジェクトの成否を分ける。単純なクラス変数への置き換えは、スレッドセーフティの観点から自殺行為だ。
移行の指針:
- コンテキストオブジェクトの導入: `EXTERNAL` な変数をすべて、「コンテキスト」という単位のクラスに集約する。CICSであれば、`COMMAREA` や `CONTAINER` にマッピングし、PL/Iの「物理的共有」から、Javaの「セッション管理」へと設計のパラダイムを移す。
- コンパイラ最適化の副作用: `OPTIMIZE(3)` などを指定している場合、コンパイラはレジスタに値を保持し続け、メモリ上の値を即座に更新しないことがある。外部からメモリを直接書き換えるようなコードがある場合、`VOLATILE` 属性(PL/Iのコンパイラオプションやアセンブラルーチン併用)の検討が必要になる場合がある。
—
最後に:レガシーは「対話」である
PL/Iのコードを読んでいると、当時のプログラマーが何を最適化しようとしていたのかが透けて見える。`EXTERNAL` 属性一つ取っても、それは単なる変数共有ではなく、限られたメモリ資源の中で最大限の処理速度を叩き出そうとしたエンジニアたちの知恵の結晶だ。
我々アーキテクトの役割は、そのコードを現代の言語に「直訳」することではない。その背後にある「システムが何を保証しようとしていたのか」という設計意図を汲み取り、現代のインフラでより安全かつ堅牢に再構築することだ。
もし今、あなたの現場で `S0C4` が頻発し、`EXTERNAL` 変数の海で溺れそうになっているなら、まずはその変数が誰によって、どのタイミングで書き換えられているのかを徹底的に追跡してほしい。その先には、必ず真実(と、修正すべきバグ)があるはずだ。
—
追伸:もしDB2埋め込みSQLとこの `EXTERNAL` 変数が混在している場合は、SQLCAのスコープにも細心の注意を払うこと。SQLCAもまた、PL/Iのリンケージにおいて思わぬ干渉を引き起こす。この話はまた、別の機会に譲ろう。
