Unix や Linux に慣れている人が IBM i を触ると、用語が分からないのではなく、 世界の区切り方が違うことで詰まります。

用語の対応表を作っても埋まりません。「ライブラリーはディレクトリーのようなもの」と 言われて進むと、三歩目で矛盾します(入れ子にならないので)。

だからこの回は、手を動かしません。先に地図だけ渡します。

いちばん大きな違い

Unix は「何でもファイル」で世界を統一しました。デバイスも、プロセスの情報も、 パイプも、全部ファイルに見せる。だから道具が少なくて済みます。

IBM i は逆方向に統一しました。「何でもオブジェクト」です。ただし オブジェクトは型を持ちます。

*LIB     ライブラリー
*FILE    ファイル(=表、または表示ファイル、またはソースの入れ物)
*PGM     プログラム
*CMD     命令そのもの
*DTAARA  データ域
*MSGQ    メッセージ待ち行列
*JOBD    ジョブ記述
...

システムが「これは何か」を知っています。 プログラムを cat できません。 できないのではなく、意味を成さない。*PGM は「バイトの列」ではなく 「実行できるもの」として存在しています。

ここが効いてきます。DSPOBJD OBJ(DEMO/*ALL) OBJTYPE(*FILE) のように、 型で絞るのが自然な操作になります。Unix で「ファイルのうち実行可能なものだけ」を 探すのに find -perm が要るのとは、出発点が違います。

で、オブジェクトって結局なに?

「型を持つファイル」と言われても腑に落ちないと思います。私もそう思います。 もう一段下りると、本当の違いが見えます。

Unix では「実行できる」は権限ビットです。

echo 'hello' > foo
chmod +x foo        # これで「実行ファイル」になる

foo の中身はただのバイトです。chmod はバイトの袋に札を貼っただけ。 カーネルは中身を知りません。実行してみて失敗するだけです。

IBM i では「プログラムである」は、そのものの正体です。

*PGM オブジェクトに勝手なバイトを書き込むことはできません。 権限が足りないのではなく、そういう操作が存在しません。オブジェクトの型ごとに 「できること」が決まっていて、それ以外の機械命令が無いのです。

Objects are encapsulated so that only valid functions are allowed for each object.

これをOSではなく、その下の層が強制しています。root でも回り込めません。

ポインターが偽造できない

もう一つ、Unix から来ると驚くところ。

IBM i のポインターは 16バイトで、メモリの語ごとにタグビットが1つ付いています。 ポインターとして有効なのは、構成するすべての語のタグが立っているときだけです。

そして、普通のデータ書き込みをするとタグが落ちます。CPU が自動で落とします。 タグを立てられるのは専用の命令だけで、それは正規の経路からしか使えません。

         普通に書き込む            タグが落ちる
ポインター ──────────────→ ただの16バイト
                             (もうポインターではない)

つまり、バッファをあふれさせて任意のアドレスを作る、ができません。 C でよくある「文字列を書きすぎて戻りアドレスを書き換える」種類の攻撃が、 構造的に成立しないということです。

機械語も、実は後から決まっている

いちばん効いているのがこれです。

IBM i のプログラムは、TIMI(Technology Independent Machine Interface)という 架空の機械の命令にコンパイルされます。本物の CPU 命令は、オブジェクトを 作るときにその下の層が翻訳して作ります。

だから何が起きたか。1995年、AS/400 は CISC から PowerPC に移りました。 利用者はソースを持っていなくても、再コンパイルせずに移行できました。 TIMI の部分が残っているので、新しい CPU 向けに翻訳し直せたからです。

Unix で言えば、x86 のバイナリーが ARM の機械でそのまま動いたようなものです。 エミュレーションではなく、作り直しで。

だから「オブジェクト」なのか

ここまで来ると、言葉の選び方が分かります。

   
ファイル バイトの袋。 意味は読む側が決める
オブジェクト 型と、できることの組。 意味は machine が持っている

プログラミング言語の「オブジェクト」に近いのです。クラスがあって、 メソッドが決まっていて、中身を勝手に触れない。それをハードウェアが強制している。

そしてこの窮屈さが、あの移植性と堅さを生んでいます。中身を触らせないから、 中身を作り直せる。

この窮屈さ 引き換えに得たもの
バイトを直接いじれない CPU を入れ替えても動く
ポインターを作れない その種の攻撃が成立しない
型の外の操作ができない 壊れたオブジェクトができない

Unix の「何でもファイル」が自由と道具の少なさを選んだのに対し、 IBM i の「何でもオブジェクト」は不自由と寿命を選んだ、ということだと思います。

このあたりは IBM i のアーキテクチャー や Below MI に詳しくあります。 日々の操作に要る知識ではありませんが、なぜこういう作りなのかが分かると、 あとの回でいちいち驚かずに済みます。

パスがない

Unix は木です。/usr/local/bin/foo。深さに制限はありません。

IBM i は2階層で終わりです。

QSYS ─┬─ MYLIB    ─┬─ QRPGLESRC   (*FILE)
      │            ├─ HELLO       (*PGM)
      │            └─ CUSTMAST    (*FILE)
      ├─ QGPL     ─── ...
      └─ QTEMP    ─── ...

QSYS がライブラリーを入れ、ライブラリーがオブジェクトを入れる。それだけ。 ライブラリーの中にライブラリーは作れません。

だから名前は ライブラリー/名前 で書きます。MYLIB/HELLO。これが絶対パスに 当たるものです。

そして名前は大文字で10文字までです。1970年代の都合ですが、いまも効いています。 MyVeryLongProgramName は置けません。

ソースファイルの中のメンバーだけ3階層目があります。 MYLIB/QRPGLESRC(HELLO) のように書きます。これは後で出てきます。

ライブラリー・リストは $PATH の拡張版

Unix の $PATH は実行ファイルを探すためのものです。データファイルは探しません。

IBM i のライブラリー・リストは、すべてのオブジェクトの探索路です。 プログラムもデータも命令も、修飾しなければここから探されます。

DSPLIBL で見えます。

Library    Type
PUB400SYS  SYS
QSYS       SYS
QSYS2      SYS
QUSRSYS    SYS
QHLPSYS    SYS
TAKAH1     CUR     ← カレント・ライブラリー
QGPL       USR
QTEMP      USR

CRTLIB LIB(MYLAB) のあと DSPLIB LIB(MYLAB) と書けるのは、MYLAB が QSYS にあり、QSYS がリストにあるからです。

これが分かると、「なぜ修飾しなくていいのか」が全部つながります。

QTEMP は private な /tmp

リストの中に QTEMP があります。これはジョブごとに1つ作られ、 ジョブが終わると消えるライブラリーです。

/tmp と違って他のジョブからは見えません。同じ名前の QTEMP が 同時に何百個あっても衝突しません。

データベースが OS の一部

Unix では、データベースは後から入れるアプリです。PostgreSQL を入れ、 デーモンを起動し、ポートに繋ぐ。

IBM i では Db2 が OS そのものです。別プロセスではありません。

そして決定的なことに、*FILE が表です。

CREATE TABLE MYLIB.CUSTOMER (ID INT, NAME CHAR(20))

これは MYLIB に CUSTOMER という *FILE オブジェクトを作ります。 DSPOBJD で見えます。WRKOBJ にも出ます。SQL で作っても、DDS で作っても、 できるものは同じ種類のオブジェクトです。

だから「ソース物理ファイル」が理解できる

最初に戸惑うのがこれです。

CRTSRCPF FILE(MYLIB/QDDSSRC) RCDLEN(112)

ソースコードを入れる「表」を作っています。 1行が1レコードで、 レコードは「連番・日付・本文」の3つの欄を持ちます。

Unix なら mkdir src に当たる操作が、IBM i では表の作成になる。 奇妙に見えますが、「何でもオブジェクト」「ファイルは表」で一貫しています。

そして RCDLEN(112) の 112 は、1行の長さです。連番6桁・日付6桁・本文100桁で 112。だから SEU の画面が80桁までしか見せないのに、ソースは100桁入ります。

ファイルはバイトの流れではない

Unix のファイルはバイトの列で、改行はただのバイトです。意味を与えるのは 読む側のプログラムです。

IBM i のファイルはレコードの並びで、レコードの形は外で決まっています。

  CUSTMAST というファイル
  ├─ CUSTNO   4桁  数字
  ├─ CNAME   20桁  文字
  └─ CSTATE  10桁  文字

この定義は DDS か SQL でファイル側に持たせます。プログラムは 「このファイルを使う」と宣言するだけで、欄を名前で触れます。

dcl-f CUSTMAST;
...
chain 1001 CUSTMAST;
// CNAME がもう使える。構造体を書く必要がない

Unix で言えば、struct がファイルに埋まっていて、コンパイラーが それを読んでくれるようなものです。

画面もオブジェクト

ここが Unix から来るといちばん驚くところかもしれません。

Unix の端末は文字の流れです。curses が差分を計算して描きます。 画面の構造はプログラムの中にあります。

IBM i では、画面の定義が別のオブジェクト(表示ファイル、*FILE)です。 DDS で書いて、別にコンパイルします。

CRTDSPF FILE(MYLIB/WTEST) SRCFILE(MYLIB/QDDSSRC)

プログラムはこう書くだけです。

dcl-f WTEST workstn;
exfmt WIN1;

EXFMT は「画面を出して、入力が返ってくるまで待つ」。1往復で完結します。

だから 5250 は遅い回線で成立しました。1文字ずつ送らず、画面まるごと送って、 まるごと返す。telnet が1文字ずつ往復するのとは設計が逆です。

そしてプログラムは欄を名前で読みます。カーソル位置の計算も、入力の 切り出しも要りません。

CL は shell ではない

===> にコマンドを打つので shell に見えますが、別物です。

命令そのものがオブジェクト(*CMD)で、引数の名前・型・既定値・ 選べる値を持っています。

CRTLIB LIB(MYLAB) TYPE(*PROD) TEXT('練習用')

LIB TYPE TEXT はキーワードで、順番を変えても通ります。型が合わなければ 実行前に弾かれます。

そして F4 を押すと引数の一覧が画面に出ます。 これは shell の補完とは違って、 命令オブジェクトが自分の引数を知っているから出せるものです。

  Unix shell CL
引数 文字列の配列。意味づけはプログラム 型と名前を持つ。システムが検査
補完 外付け(bash-completion) 命令自身が持っている(F4)
組み合わせ パイプで繋ぐ パイプは無い。オブジェクトを介す

パイプが無いのは不便に見えますが、発想が違います。Unix は小さな道具を 文字列で繋ぎます。IBM i はオブジェクトで繋ぎます。DSPOBJD の結果を 画面ではなくファイルに出して、それを別のプログラムが読む。

DSPOBJD OBJ(MYLIB/*ALL) OBJTYPE(*ALL) OUTPUT(*OUTFILE) OUTFILE(QTEMP/OBJS)

これが IBM i のパイプです。中間物に型があるぶん、壊れにくい代わりに 手数が増えます。

プロセスではなくジョブ

Unix のプロセスは軽く、fork して死にます。

IBM i のジョブはもっと重い概念で、いろいろ抱えています。

   
ライブラリー・リスト ジョブごとに違う
QTEMP ジョブごとに1つ
ジョブログ 起きたメッセージの記録
サブシステム どこで動いているか。対話は QINTER
ジョブ記述(*JOBD) 既定値のかたまり

サインオン画面に Subsystem . . . : QINTER2 と出ていたのは、 あなたのジョブがそこで動いているという意味です。

エラーはメッセージ

Unix は終了コードと stderr です。意味は呼ぶ側が決めます。

IBM i はメッセージです。IDを持ちます。

CPF7302  File WTEST not created in library TAKAH1.
CPD7830  Start line number too large.

CPF CPD は出どころ、数字は番号。メッセージはメッセージ・ファイルという オブジェクトに入っていて、説明文も原因も回復方法も持っています。

DSPMSGD RANGE(CPF7302)

で読めます。プログラムからも捕まえられます。

MONMSG MSGID(CPF0000)

Unix の set -e や trap に当たるものですが、IDで捕まえるのが違いです。

単一レベル記憶

最後に、見えないけれど根っこにあるものを。

Unix はメモリとディスクを区別します。malloc と open は別世界で、 mmap で橋を架けます。

IBM i は区別しません。すべてのオブジェクトが1つの巨大なアドレス空間に あり、メモリに載っているかディスクにあるかは OS が勝手に決めます。 プログラムからはどちらも同じです。

だから「オブジェクトがある」という言い方になります。ファイルを「開く」のは、 バイトの流れを得るためではなく、そのオブジェクトを使う権利を得るためです。

これは普段は意識しません。ただ、なぜ IBM i の話し方が「ファイル操作」ではなく 「オブジェクト管理」なのかの理由はここにあります。


持って帰る1枚

Unix IBM i
ファイル(バイトの流れ) オブジェクト(型を持つ)
ディレクトリーの木 ライブラリー(2階層で終わり)
$PATH(実行ファイルだけ) ライブラリー・リスト(すべて)
/tmp QTEMP(ジョブ専用、消える)
後から入れる DB Db2 が OS の一部。*FILE が表
改行区切りのテキスト レコード(形は外で決める)
curses が端末に描く 表示ファイルを別にコンパイル、EXFMT で1往復
shell とパイプ *CMD オブジェクトと、中間ファイル
プロセス ジョブ(リスト・QTEMP・ログ・サブシステム)
終了コードと stderr メッセージ(IDを持つ)
メモリとディスクは別 単一レベル記憶

全部を今すぐ飲み込む必要はありません。詰まったときにここへ戻ってくるための 地図です。

次の回から手を動かします。