ロードモジュール
- 1 ジョブとして実行できるもの
-
Windowsではxxx.exeとかxxx.com、xxx.batなどのファイルが、プログラムとして実行することができます。それに対してA−VXで実行できるものは、ロードモジュール、ジョブストリーム、パラメータメンバの3種類があります。
このうちジョブストリームとパラメータメンバについては説明済みです。 ここではロードモジュールについて説明します。
- 2 ロードモジュール(LM)とは
-
Windowsでxxx.exeやxxx.comに相当するものが、ロードモジュールです。COBOL言語やFORTRAN言語でソースプログラムを作り、それをコンパイル/リンクした後にできる実行形式のものがロードモジュール(LM)です。A−VX付属のいろいろなユーティリティ(#ABCとか#MAPとか)もロードモジュールです。
ロードモジュールはロードモジュールライブラリ(LML)に格納します。
ロードモジュールは、ジョブとしてRUNコマンドやJCLの/RUN文、メニューなどから実行します。
- 3 ロードモジュール(LM)の構造
-
ロードモジュールを起動・実行するうえではロードモジュールの構造など知っていなくても問題ありません。
プログラムエラー発生時にもしかしたら役に立つかも、程度です。
後はソースプログラムをコンパイル/リンクするときに「このパラメータは何か?」と思ったときに役に立つかもしれない、程度でしょうか。ロードモジュールをA−VXはセグメントという単位で管理しているようです。ロードモジュールを1つあるいはいくつかのセグメントに分割してLMLに格納しておき、ロードモジュール実行時に必要になる都度セグメントをメモリに読み込んで実行していくという仕組みのようです。
セグメント内もいろいろなものが混在しているわけではなく、このセグメントはデータ部、このセグメントはメインルーチンの部分、こっちのセグメントはコールされたプログラムの部分と分けられています。
セグメントのサイズは最大64キロバイトの可変の長さのようです。大きいプログラムなら64キロバイトのサイズのセグメントが複数個、小さいプログラムなら4キロバイトのセグメントが1個だけとかになります。
セグメントは2種類あるようです。
1つめはノンシェラブルセグメント。書き込みや読み込みができるセグメントで、データ部のようにデータを書いたり読んだりするような目的に使うセグメントのようです。Non-shareable、つまり実行したプログラム毎に状態が違うので共有できないセグメントということでしょうかね。
もう1つはリエントラントセグメント。書き込みができないセグメントで、命令部のように書き換わらない部分に使われるセグメントのようです。命令が勝手に書き換わったらウイルスみたいで嫌ですからね。re-entrantということでしょう。
ロードモジュールのセグメントの個数やそれぞれのサイズ、セグメントの種類などの情報はリンク時に出力されるマップを見ればわかります。
- 4 ロードモジュール(LM)の種類
-
ロードモジュールは4種類あるようです。
- 5 混合型LM
-
セグメント内にいろいろなものが混在しているわけではないと書いておきながら、いきなり混合型です。
1つのセグメント内にデータ部と命令部(手続き部)が混在しています。といってもリンカが出力するリンクマップを見るとセグメントの前半がデータ部、後半が命令部と分けられているようです。1つのセグメントで構成され、サイズは最大63キロバイトです。
他の種類のロードモジュールとは実行モードが異なるので、実行スピードが劣るそうです。
このタイプだと最大でも63キロバイト分の大きさのプログラムしか作れません。 はるか昔1970年や1980年頃はオフコンの搭載メモリも1メガバイトあるかどうかというものでした。(今はパソコンでもメモリは数ギガバイトありますよね。)その頃に誕生したタイプのLMです。1メガバイトもないようなメモリでも実行できるタイプです。
なので今は積極的にこのタイプのLMを作るような場面はありません。A−VX02のような最新のOSでも1970年や1980年頃に作ったロードモジュールがそのまま動きます、ということを表します。
ユーティリティにも混合型LMのものがいくつもあります。今でも動いている例としてよく挙がるので#BEDITで1セグメントでそのサイズは12キロバイトぐらい、#ALLOCがサイズが63キロバイトぐらいです。
混合型LMもさらに細かく分類すると4つの形態に分かれるようです。
混合型LMの4形態 形 態 概 要 シンプル構造ノンシェアラブル型LM 最も単純な構造のLM リージョン構造LM 手続き部をリージョン構造にオーバレイしているLM トリー構造LM 手続き部をトリー構造にオーバレイしているLM シンプル構造リエントラント型LM シンプルノンシェアラブルをリエントラント化したLM 形態別に細かい説明もいまさら不要だと思うのでこれ以上は立ち入りません。
- 6 分離型LM
-
ノンシェラブルセグメント(つまりデータ部)のセグメントが1つ、リエントラントセグメント(つまり命令部)のセグメントが1つの計2個のセグメントで構成されるLMです。
データ部が最大63キロバイト、命令部が最大63キロバイトのプログラムまではこの分離型LMとして作ることができます。
これも古いタイプのロードモジュールで、たぶん「混合型LMだと大きなサイズのプログラムができない」と言われて作ったタイプかなと思います。でも2つのセグメント合わせても126キロバイトのサイズのロードモジュールしか作れません。
分離型LMも混合型LMと同様に実行スピードが劣るそうなので、今コンパイル/リンク時に積極的にこのタイプを選択する必要はありません。
混合型LMと同様に従来互換のためのLMです。
ユーティリティにもいくつか分離型LMがあります。#ABANAはノンシェラブルセグメントが63キロバイト、リエントラントセグメントが32キロバイトの分離型LMです。
ちなみにA−VX02でもこのタイプのLMを作ることはできます。COBOLコンパイラ(COBOL74規格の古い方のコンパイラね)でCSGパラメータにSEPARATE指定してコンパイルし、リンクするとできます。
分離LMもさらに細かく分類すると3つの形態に分かれるようです。
分離型LMの3形態 形 態 概 要 シンプル構造ノンシェアラブル型/リエントラント型LM データ部と手続き部が分かれた単純構造のLM リージョン構造LM 手続き部をリージョン構造にオーバレイしているLM トリー構造LM 手続き部をトリー構造にオーバレイしているLM 分離型LMも今新規に作ることもあまりないため、形態別に細かい説明もいまさら不要だと思うのでこれ以上は立ち入りません。
- 6 複数分離型LM
-
これが今の標準のタイプのLMです。
複数のセグメント構成で最大6メガバイトのLMを作ることができます。
ノンシェラブルセグメントがデータ部を集めたセグメント、リエントラントセグメントが命令部を集めたセグメントです。
最小構成でノンシェラブルセグメント1個、リエントラントセグメント1個の2セグメント構成。セグメントの組み合わせは自由で、例えばデータ部が多いならノンシェラブルセグメントが20個、リエントラントセグメントが1個みたいな構成もできます。
1セグメントのサイズは最小4キロバイト、最大64キロバイト。
COBOL85コンパイラでLMを作るとデフォルトでこのタイプのLMになります。
複数分離型LMもさらに細かく分類すると2つの形態に分かれるようです。
分離型LMの2形態 形 態 概 要 シンプル構造ノンシェアラブル型/リエントラント型LM データ部と手続き部が分かれた単純構造のLM リージョン構造LM 手続き部をリージョン構造にオーバレイしているLM オーバレイ構造のLMは、1個の常駐セグメントと複数の非常駐セグメントから構成され、LMの実行時、常駐セグメントは常時メモリに存在し、非常駐セグメントは制御が移るたびにメモリにロードされます。非常駐セグメントはシステムが自動的に必要になればメモリ上に読み込み、不要になればメモリ上から削除されるため、メモリ空間を節約できます。
今のオフコンはメモリは数ギガバイト搭載しています。1セグメント64キロバイトです。(1キロ=0.001メガ=0.00001ギガです)それが複数あったとしてもメモリの節約量はわずかです。使用できるメモリが少なかった昔に使用メモリを節約するために用意された形態です。オーバーレイ構造のLMはメモリにロードしたり削除したりという操作が入るので、シンプル構造よりも動作が遅くなります。
シンプル構造のLMを作れば問題ないです。今ならオーバレイで作る必要はありません。オーバレイ構造はセグメント構成を考える必要があるので面倒です。 - 6 高速型LM
-
特殊なLMで、このタイプはシステムユーティリティしかないようです。
構造は複数分離型LMと同じなようです。普通のLMよりも高速に実行できるタイプのLMのようです。
調べたところ#MERGEや#SORTがこのタイプでした。
A−VX3時代に一時的にこのタイプのLMを出力する専用のコンパイラがありましたが、カスタマイズしすぎたのかこのコンパイラで出力したLMはA−VX4以降で動きません。このような問題があったからなのかA−VX4以降ではこのコンパイラは使用できない(インストールできない)&販売中止になっています。
私はこのコンパイラは製品説明ぐらいしか見たことがないので、詳細はよくわかりません。 - 7 ロードモジュールのセグメントを深堀してみる
-
COBOLプログラムをコンパイルしてリンクするとロードモジュールができます。
リンクするときに出力されるリンクマップを見るとセグメントの状態がわかります。それで少しセグメントについて説明したいと思います。
下があるプログラムをリンクした結果出力されたリスト(の一部)です。使用するところだけ載せています。
「MAP LIST」から「END OF MAP LIST」までがリンクマップです。COBOL85で作成したプログラムをリンクしました。複数分離型LMです。
MAP LIST RESIDENT PART ******************************* *** DATA PART ****************************** ******** SEGMENT 0000 (0004) SIZE = 00001928 ( 6440) FILE CU-NAME REV. COMPILED ATR LANG SIZE FROM -TO ADDR EXT/COMM SIZE SEG. STAC 000002A0 ( 672) 00040100-0004039F SCF LNK@OV 0018 62/03/14 DATA SYS 0000001A ( 26) 000403A0-000403B9 LNK@OVSG 000403B4 SCF SYS@ID V101 94/08/11 S006 SYS 00000002 ( 2) 000403BC-000403BD SCF SYS@ID V101 94/08/11 S001 SYS 00000634 ( 1588) 000403C0-000409F3 SYS@IDPB 000405E4 CUF1 PERENT 0003 26/05/26 S100 CBL 000000AC ( 172) 000409F4-00040A9F 0005099C COB@4APR 0001 000509FC COB@0VDP 0001 00050878 CHILD1 0001 000508F0 CHILD2 0001 00050BC8 COB@0UAP 0001 00050D90 COB@0XSR 0001 00050DE8 COB@0BEP 0001 CUF1 CHILD1 0002 26/05/26 S100 CBL 00000064 ( 100) 00040AA0-00040B03 0005099C COB@4APR 0001 000509FC COB@0VDP 0001 00050DE8 COB@0BEP 0001 00050D90 COB@0XSR 0001 CUF1 CHILD2 0002 26/05/26 S100 CBL 0000009C ( 156) 00040B04-00040B9F 0005099C COB@4APR 0001 00050BC8 COB@0UAP 0001 000509FC COB@0VDP 0001 00050DE8 COB@0BEP 0001 00050D90 COB@0XSR 0001 SCF SYS@0V 0022 01/03/16 DATA SYS 00000074 ( 116) 00040BA0-00040C13 00050CB0 COB@9AAB 0001 00050F0C COB@9WWK 0001 SCF SYS@0U 0006 87/04/21 DATA SYS 0000004C ( 76) 00040C14-00040C5F 00050CB0 COB@9AAB 0001 00050F0C COB@9WWK 0001 SCF SYS@9A 0005 91/05/21 DATA SYS 00000074 ( 116) 00040C60-00040CD3 COB@9ACM 00040C60 COB@9ACC 00040C60 COB@9ASV 00040C70 COB@9AAB 00040CB0 COB@9ADB 00040CC0 SCF SYS@IT 0001 84/07/29 DATA SYS 00000010 ( 16) 00040CD4-00040CE3 SCF SYS@9W 0006 93/03/08 DATA SYS 00000B28 ( 2856) 00040CE4-0004180B COB@9WDT 00040CE4 COB@9WWK 00040F0C COB@9WPS 0004170C SCF SYS@9G 0003 94/08/09 DATA SYS 0000011C ( 284) 0004180C-00041927 COB@9GEP 0004180C COB@9GCA 00041910 COB@9GDB 00041914 *** PROC PART ****************************** ******** SEGMENT 0001 (0005) SIZE = 00000E74 ( 3700) FILE CU-NAME REV. COMPILED ATR LANG SIZE FROM -TO ADDR EXT/COMM SIZE SEG. SCF LNK@OV 0018 62/03/14 PROC SYS 000000AA ( 170) 00050000-000500A9 LNK@OVCA 00050026 LNK@OVLD 00050024 LNK@OVBC 00050000 LNK@OVBL 00050012 LNK@OVIT 00050070 SCF SYS@ID V101 94/08/11 S003 SYS 000006FC ( 1788) 000500AC-000507A7 SYS@ID 000500AC SYS@IDPT 0005061A CUF1 PERENT 0003 26/05/26 S000 CBl 000000D0 ( 208) 000507A8-00050877 PERENT 000507A8 CUF1 CHILD1 0002 26/05/26 S000 CBl 00000078 ( 120) 00050878-000508EF CHILD1 00050878 CUF1 CHILD2 0002 26/05/26 S000 CBl 000000AA ( 170) 000508F0-00050999 CHILD2 000508F0 SCF SYS@4A 0004 84/07/28 PROC SYS 0000005E ( 94) 0005099C-000509F9 COB@4APR 0005099C 00040CB0 COB@9AAB 0000 00050E34 SYS@ITEM 0001 00040F0C COB@9WWK 0000 00040C60 COB@9ACM 0000 SCF SYS@0V 0022 01/03/16 PROC SYS 000001CC ( 460) 000509FC-00050B COB@0VDP 000509FC SCF SYS@0U 0006 87/04/21 PROC SYS 000001C6 ( 454) 00050BC8-00050D COB@0UAP 00050BC8 SCF SYS@0X 0001 92/03/30 PROC SYS 00000056 ( 86) 00050D90-00050D COB@0XSR 00050D90 00040CB0 COB@9AAB 0000 00041910 COB@9GCA 0000 00040C60 COB@9ACM 0000 SCF SYS@0B 0003 87/04/20 PROC SYS 0000004A ( 74) 00050DE8-00050E COB@0BEP 00050DE8 00040CB0 COB@9AAB 0000 00050E34 SYS@ITEM 0001 SCF SYS@IT 0001 84/07/29 PROC SYS 0000003E ( 62) 00050E34-00050E SYS@ITEN 00050E34 END OF MAP LIST LOAD SEGMENT GRAPH LIST LM FORM = ADM 2 RESIDENT PART ******************* **** DATA PART ****************** LSA = 004 LSEG-ID. ADDR SIZE:HEX(DEC) MAIN CU ....-....1....-....2 LSEG0000 0000 01928( 6440) PERENT ******* **** PROC PART ****************** LSA = 005 LSEG-ID. ADDR SIZE:HEX(DEC) MAIN CU ....-....1....-....2 LSEG0001 0000 00E74( 3700) PERENT **** END OF LOAD SEGMENT GRAPH LIST
まず大きく見るとDATA PARTとPROC PARTに分かれています。
DATA PARTがデータ部(つまりノンシェラブルセグメント)のセグメント群、PROC PARTが命令部(つまりリエントラントセグメント)のセグメント群になります。もう少し詳細にみていきましょう。 DATA PARTの次の行に「SEGMENT 0000 (0004)」と書いてあります。ここから下はSEGMENT 0000の情報になります。0000はセグメント番号と言ってセグメントに順番につけられている番号です。セグメント番号が0のセグメントの情報になります。 DATA PARTにはセグメント番号0のセグメントしかありません。もしDATA PART中にSEGMENT 0001とかSEGMENT 0002とかがあれば、データ部に複数のセグメントがあることになります。今回はデータ部のセグメントは、セグメント番号0のセグメントが1つだけということになります。
次にPROC PARTを見てみます。「SEGMENT 0001 (0005)」があります。命令部にはセグメント番号0001のセグメントが1つだけあります。
今回はデータ部のセグメントが1つ、命令部のセグメントが1つ、合計2つのセグメントを持つ一番単純な複数分離型LMでした。(分離型LMも2つのセグメントを持ちますが、今回は複数分離型LMを指定してリンクしているので複数分離型LMです。)
データ部と命令部のセグメントは連番になっています。例えばデータ部のセグメントが4つ、命令部のセグメントが3つあるときは、データ部はセグメント番号0000、0001、0002、0003、命令部はセグメント番号0004、0005、0006のセグメントがあるはずです。
「SEGMENT 0000 (0004)」の括弧の中の4桁の数字はLSA番号といいます。セグメントをメモリ上に読み込んだ時の番号です。セグメント番号とはまた違う番号です。
シンプル構造ノンシェアラブル型/リエントラント型LMの場合は、セグメント番号+4の値がLSA番号になります。単純構造なので。
リージョン構造LMの場合は、オーバレイしているセグメントは同じLSA番号になります。例えばセグメント番号0008とセグメント番号0009がオーバーレイしているのならば「SEGMENT 0008 (0012)」「SEGMENT 0009(0012)」のように同じLSA番号になります。
「SEGMENT 0000 (0004)」の次にLNK@OVとかSYS@IDとかPERENTとかいろいろ書いてある行があります。これが何かというと、このセグメントを構成しているソースプログラムの名前です。xxx@xxxとなっているのはたいていシステムが用意したプログラムです。あなたが作ったCOBOLプログラムをリンクするとシステムが必要なプログラムをいろいろとくっつけてロードモジュールを作ってくれます。
私が作ったのはPERENT、CHILD1、CHILD2という3つの小さなプログラムだけなのですが、リンク時にシステムが必要なプログラムを自動的にくっつけてくれました。
セグメント番号0000のセグメントは12個のプログラムから構成されています。セグメント番号0001のセグメントは11個のプログラムから構成されています。
このようにロードモジュールは、複数のセグメント(混合型LMは1つのセグメント)から構成され、そのセグメントの中もたくさんのプログラムで構成されています。
「LOAD SEGMENT GRAPH LIST」から「END OF LOAD SEGMENT GRAPH LIST」の間はロードセグメントのサイズなどの情報をまとめたものです。
「LM FORM = ADM 2」の意味ですが、ADM2は複数分離型LMを意味します。ADM0が混合型、ADM1が分離型です。
「DATA PART」の「LSA = 004」はLSA番号が0004ということ。「LSEG0000 0000 01928( 6440) PERENT」はLSA番号が0004のセグメントはセグメント番号0000の1つだけで、そのサイズは十進数で6440バイト、メインのCUはPERENT。
「PROC PART」の「LSA = 005」はLSA番号が0005ということ。「LSEG0001 0000 00E74( 3700) PERENT」はLSA番号が0005のセグメントはセグメント番号0001の1つだけで、そのサイズは十進数で3700バイト、メインのCUはPERENT。