Unix 使いのための IBM i 地図
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を持つ) |
| メモリとディスクは別 | 単一レベル記憶 |
全部を今すぐ飲み込む必要はありません。詰まったときにここへ戻ってくるための 地図です。
次の回から手を動かします。