more unix/v6

おいしいビールのつまみ、第2弾。

冷や飯がわずかに残ってしまう事がある。そういうのは、普段は冷凍して保存しておき、 ある程度溜まると、昼のチャーハンに化ける事が多い。でも、たまにはその場で使ってしまおう。

冷や飯を、小さし一杯分ぐらいづつ取り分けて、クッキング用の耐熱シートの上に適当な 間隔で並べます。その上にまた耐熱シートをかぶせ、上からすりこぎでローラーします。 そう、ご飯を潰して、おせんべい状にするんです。おせんべいの上には、お好みで、ゴマ塩 とか振ります。

後は、オーブンにシート毎入れて、焦げ目が付くぐらいに焼きます。冷ますと、ぱりぱりに なって、ビールのおつまみやおやつに最適です。

この製法のバリエーションで、ご飯の代わりにチーズでもOKです。チーズの場合は、トッピングに 小女子や干し海老等が最適です。油を一切使っていないので、その分余計にビールを飲んでも、きっと大丈夫(何が?)

外の世界と交信しろ

この間から、いにしえのunixで遊んでもらっているんだけど、外の世界(FreeBSD等)と ファイルのやり取りはどうやるって疑問が出てきた。イーサネットはまだ使えんし。。。

昔のMZ-80時代を思い出すと、記憶媒体はカセットテープだったよな。昔のコンピュータは カセットテープの親玉、MT装置が付いていたじゃん。きっとpdp-11にも接続出来るに違いない。

2238クラブさんのUNIX第6版を動かす を見ていると、att tm0 dump.tap と言うのをシュミレータに渡してる。tm0ってひょっとして テープ装置? きっとそうだろう。これも付けて起動したけど、はてどうやって使うのやら?

取り合えず、/devにそれらしいデバイスが有るかだな。無いと始まらないもん。

# ls -l /dev
total 0
crw-r-----  1 bin     8,  1 Jan  1  1970 kmem
crw-r-----  1 bin     8,  0 Jan  1  1970 mem
crw-rw-rw-  1 bin     8,  2 Jan  1  1970 null
brw-r--r--  1 bin     0,  0 Jan  1  1970 rk0
brw-r--r--  1 bin     0,  1 Jan  1  1970 rk1
brw-r--r--  1 bin     0,  2 Jan  1  1970 rk2
brw-r--r--  1 bin     0,  3 Jan  1  1970 rk3
brw-r--r--  1 bin     0,  4 Jan  1  1970 rk4
brw-r--r--  1 bin     0,  5 Jan  1  1970 rk5
brw-r--r--  1 bin     0,  6 Jan  1  1970 rk6
brw-r--r--  1 bin     0,  7 Jan  1  1970 rk7
crw-r--r--  1 bin     9,  0 Jan  1  1970 rrk0
crw-r--r--  1 bin     9,  1 Jan  1  1970 rrk1
crw-r--r--  1 bin     9,  2 Jan  1  1970 rrk2
crw-r--r--  1 bin     9,  3 Jan  1  1970 rrk3
crw-r--r--  1 bin     9,  4 Jan  1  1970 rrk4
crw-r--r--  1 bin     9,  5 Jan  1  1970 rrk5
crw-r--r--  1 bin     9,  6 Jan  1  1970 rrk6
crw-r--r--  1 bin     9,  7 Jan  1  1970 rrk7
crw--w--w-  1 root    0,  0 Aug 20 14:52 tty8

やっぱり無いや! 私のかすかな記憶では、mknodとかでデバイスファイルを作ったような。 そう思って、manの代わりのpdfを調べてみると、メジャーデバイス番号が作成に必要とか 書いてある。(マイナー番号も必要だけど、まあ、ゼロでいいよね)

困った時はソースを喰え。栄養分満点だから。 V6/usr/sys/conf/mkconf.c を見よう。何か分かるだろう。

char	*btab[]
{
	"rk",
	"rp",
	"rf",
	"tm",
	"tc",
	"hs",
	"hp",
	"ht",
	0
};
char	*ctab[]
{
	"console",
	"pc",
	"lp",
	"dc",
	"dh",
	"dp",
	"dj",
	"dn",
	"mem",
	"rk",
	"rf",
	"rp",
	"tm",
	"hs",
	"hp",
	"ht",
	0
};

rknのメジャー番号は0、rrknのメジャー番号は9となってる事から、どうやら配列のインデックス 番号がメジャー番号になるようだ。ブロックデバイスと言う事なんで、btabのtmの位置を 見ると、3番になっている。そんな訳なんで、

# cd /dev
# /etc/mknod mt0 b 3 0
# ls -l mt0
brw-rw-rw-  1 root    3,  0 Aug 20 14:54 mt0
# chown bin mt0
# ls -l mt0
brw-rw-rw-  1 bin     3,  0 Aug 20 15:06 mt0

パーミションもちゃんと設定しないとまずいだろうけど、心は広くだれでも使えるように 公開しておく事にしよう。さて、試運転は、ddコマンドかな。

# cd /
# dd if=CATLOG of=/dev/mt0
48+1 records in
48+1 records out

以前作っておいたファイルのカタログをdumpしてみた。

[sakae@cdr ~/UV6]$ dd if=dump.tap of=CATLOG
49+1 records in
49+1 records out
25484 bytes transferred in 0.000521 secs (48918830 bytes/sec)
[sakae@cdr ~/UV6]$ head CATLOG
/
/unix
/rkunix
/dev
/dev/rk0
/dev/rk1
/dev/rk2
/dev/rk3
/dev/rk4
/dev/rk5

それをFreeBSD側で読んでみた。ブロックサイズが一つ異なっているのは何故? 細かい事は 気にせずに、確認した。ちゃんと読めてるね。そんじゃ、逆にFreeBSD側データを持って 行こう。

[sakae@cdr ~/UV6]$ dd if=to of=dump.tap
0+1 records in
0+1 records out
114 bytes transferred in 0.000115 secs (989960 bytes/sec)
# dd if=/dev/mt0 of=fromBSD
Invalid magtape record length, PC: 021712 (RTS PC)
sim>

ありゃりゃ、未来からデータを輸入する事は、歴史を歪める事になっているので拒否 されました。なんて、SFの世界じゃないんだから。kenにバグレポートを送った方が 良いのでしょうか?

絶望に打ちひしがれて、我 tar を探さん。

って言っても、この頃のunixには、そんな便利なのは無い。FreeBSDでman tarしてみると

歴史
     tar コマンドは Seventh Edition Unix から登場しました。他の多数の実装があ
     り、その多くはファイルフォーマットを拡張しています。 John Gilmore による
     パブリックドメイン実装の pdtar (1987 年 11 月頃) は多大な影響を及ぼし
     、GNU tar の元になりました。 FreeBSD 1.0 より、GNU tar は FreeBSD 基本シ
     ステムの tar として取り込まれました。

と言う事で、次世代まで待たんといかんとよ。そんじゃ、6thでは、ddしか使えないかと 言うと、tp なんて言う、tarの始祖がありました。

# cd /usr/source/sno
# ls
run
sno.h
sno1.c
sno2.c
sno3.c
sno4.c
# tp crm0 run sno.h sno?.c
   6 entries
  48 used
 110 last
END
# tp tvm0
   mode    uid gid tapa    size   date    time name
rw-r--r--   3   1   63       74 70/01/01531:00 run
rw-r--r--   3   1   64      338 70/01/01531:00 sno.h
rw-r--r--   3   1   65     6305 70/01/01531:00 sno1.c
rw-r--r--   3   1   78     7723 70/01/01531:00 sno2.c
rw-r--r--   3   1   94     3644 70/01/01531:00 sno3.c
rw-r--r--   3   1  102     4176 70/01/01531:00 sno4.c
   6 entries
  48 used
 110 last

マニュアルを紐解き、tpコマンドを使ってみました。tpの第一引数が動作を示し、第二引数 以降が、ファイルとかになります。crでフレッシュなテープに書き出せ。m0はテープ装置番号と なります。現代風に翻訳すると、tar cf /dev/mt0 ですね。次の、tp tvm0は、確認ですね。

# cd /tmp
# tp xvm0
x run
x sno.h
x sno1.c
x sno2.c
x sno3.c
x sno4.c
END
# ls -l
total 51
-rw-r--r--  1 bin        74 Aug 20 14:58 run
-rw-r--r--  1 bin       338 Aug 20 14:58 sno.h
-rw-r--r--  1 bin      6305 Aug 20 14:58 sno1.c
-rw-r--r--  1 bin      7723 Aug 20 14:58 sno2.c
-rw-r--r--  1 bin      3644 Aug 20 14:58 sno3.c
-rw-r--r--  1 bin      4176 Aug 20 14:58 sno4.c

場所を移して、今度は展開しました。ちゃんと展開出来ています。なお、このテープ(FreeBSD 上で言う、dump.tapは、勿論BSD上では使えません。後、このdump.tapは、作業の度にcreat されないようなので、注意が必要です。)

buildkernel

ふと、悪戯心で、 V7/usr/src/cmd/tar から移植出来ないか見たんだ。そしたら、Makefileも提供されてた。って事は、7thからが 我々が普通に目にするunixの始まりなのね。Makefileを見ると、v6の互換モードも提供 されてるみたい。v6も初期と後期じゃ機能が違ったのね。頑張ってv6libも一緒に持ってくれば コンパイル出来るのだろうか? 芋ヅル式にいろいろなファイルを要求されて、収拾が付かなく なったりして。いつか7thも動かしてみたいな。

ついでなので、6thのkernelはどうやってコンパイルされたか見ておく。場所は、 V6/usr/sys/run

chdir ken
cc -c -O *.c
ar r ../lib1
rm *.o

chdir ../dmr
cc -c -O *.c
ar r ../lib2
rm *.o

chdir ../conf
as m40.s
mv a.out m40.o
: as m45.s
: mv a.out m45.o
: cc sysfix.c
: mv a.out sysfix
cc mkconf.c
mv a.out mkconf

mkconf
rk
tm
tc
done

cc -c c.c
as l.s
ld -x a.out m40.o c.o ../lib1 ../lib2
: as data.s l.s
: ld -x -r -d a.out m45.o c.o ../lib1 ../lib2
: nm -ug
: sysfix a.out x
: mv x a.out
cmp a.out /rkunix
cp a.out /rkunix
 ;

このrunと言うシェルスクリプト、起動する時は、sh runってやっているけど、この頃は、#!/bin/sh と言う、いわゆるシェバングが使えなかったみたい。edで#を入力しようとしても無視されるし。。。 上の例にもあるけど、先頭行の コロンは、コメントだったのかな。

kenに入って、*.cをコンパイルし、arコマンドを使ってlib1を作成。ハード関係の所に行って 同様にlib2を作成。続いてconfに入ってmkconfコマンドを作成。mkconfにrk,tm,tcの引数を 与えてコンパイル。rkはDisk、tmはテープドライブ(オープンリール?)、tcはDECTape(カートリッジ式) になるのかな。多分これで、デバイスが組み込まれたc.cが出来ると。

そいつをコンパイルするんだけど、直接a.outを作成出来ないので、一度アセンブラにの出力を 出して、それをアセンブルして、a.outを作成。(c.cも使うよ)最後に、作っておいたライブラリーと 一緒にリンクする。これでカーネルが出来上がったので、rk用のunixっつう事で登録。 ここには、現れていないけど、rkunixと言うファイルをunixとして手動でコピーするんだろうな。 なお、同様な手順で違うタイプのDisk用にも作っているけど、省略したよ。

boot

カーネルの作り方は分かった。そんじゃ、kernelをDiskから読み込んで起動する、いわゆるbootは どうなっている? いろいろ探し回ったら、/usr/source/mdecに有った。

login: ROOT
# CD /USR/SOURCE/MDEC
# LS -L
TOTAL 65
-RW-R--R--  1 BIN       740 JAN  1  1970 DLDR.S
-RW-R--R--  1 BIN      3794 JAN  1  1970 DTF.S
-RW-R--R--  1 BIN      2283 JAN  1  1970 FSBOOT.S
-RW-R--R--  1 BIN       485 JAN  1  1970 HP.S
-RW-R--R--  1 BIN       728 JAN  1  1970 HT.S
-RW-R--R--  1 BIN      2646 JAN  1  1970 MBOOT.S
-RW-R--R--  1 BIN       634 JAN  1  1970 MCOPY.S
-RW-R--R--  1 BIN        19 JAN  1  1970 RESET.S
-RW-R--R--  1 BIN        28 JAN  1  1970 RHP.S
-RW-R--R--  1 BIN       194 JAN  1  1970 RK.S
-RW-R--R--  1 BIN       437 JAN  1  1970 RKF.S
-RW-R--R--  1 BIN       249 JAN  1  1970 RP.S
-RW-R--R--  1 BIN        27 JAN  1  1970 RRK.S
-RW-R--R--  1 BIN        27 JAN  1  1970 RRP.S
-RW-R--R--  1 BIN      1041 JAN  1  1970 RUN
-RW-R--R--  1 BIN      2646 JAN  1  1970 TBOOT.S
-RW-R--R--  1 BIN       451 JAN  1  1970 TC.S
-RW-R--R--  1 BIN      3764 JAN  1  1970 TCF.S
-RW-R--R--  1 BIN       617 JAN  1  1970 TM.S
-RW-R--R--  1 BIN      1016 JAN  1  1970 TPBOOT.S
-RW-R--R--  1 BIN       738 JAN  1  1970 TTY.S
-RW-R--R--  1 BIN      2447 JAN  1  1970 UBOOT.S
-RW-R--R--  1 BIN        29 JAN  1  1970 WHP.S
-RW-R--R--  1 BIN        28 JAN  1  1970 WRK.S
-RW-R--R--  1 BIN        28 JAN  1  1970 WRP.S

大文字でloginすると、以後大文字で応答します。昔の端末は、小文字が使えないのが有った そうで、そういう端末への配慮とか。今のunixでもこの動作は受け継がれているのかな?

# GREP UNIX *
DTF.S:/ UNIX DEC-TAPE TIME/MTRACK TRACK FORMATTER
FSBOOT.S:/ TO A UNIX FILE SYSTEM ENTRY
MBOOT.S:        CMP     (R0),$407               / UNIX A.OUT?
TBOOT.S:        CMP     (R0),$407               / UNIX A.OUT?
TCF.S:/ UNIX DEC-TAPE TIME/MTRACK TRACK FORMATTER
UBOOT.S:/ TO A UNIX ENTRY

pdp11からunixを起動する時、@ に続いて unixって入れるんで、その痕跡が無いかと grepしてみた。検索文字の大文字小文字の処理はどうしてるんだろう? 等と考えつつ、 関係ありそうなのは、fsboot.sかなあ、などと当たりを付ける。

/ read in path name
/ breaking on '/' into 14 ch names

        mov     $trvect,r5
        mov     $'@,r0
        jsr     pc,(r5)
        mov     $names,r2
1:
        mov     r2,r1
2:
        jsr     pc,getc
        cmp     r0,$'\n
        beq     1f
        cmp     r0,$'/
        beq     3f
        movb    r0,(r1)+
        br      2b
3:
        cmp     r2,r1
        beq     2b
        add     $14.,r2
        br      1b

@を出した後、起動するカーネル名を読み込んでいるのかな。ジャンプ命令の後のラベルは 飛び先なんだろうけど、いまいち分からんな。もう少し、やさしそうなアセンブラソースを みるかな。

やさしいアセンブラ

普通のコマンドの代表として、先に出てきた mknodを調べてみる。このコマンドは、s2/mknod.cと s5/mknod.sのコンビみたいだ。

monod.cは何の事はなく、核心部分は

        if(*argv[2] == 'b')
                m = 0160666; else
        if(*argv[2] == 'c')
                m = 0120666; else
                goto usage;
        a = number(argv[3]);
        if(a < 0)
                goto usage;
        b = number(argv[4]);
        if(b < 0)
                goto usage;
        if(mknod(argv[1], m, (a<<8)|b) < 0)
                perror("mknod");

だけだ。mknod.sは、アセンブラで書かれている。

/ C library -- mknod

/ error = mknod(string, mode, major.minor);

.globl  _mknod, cerror

mknod = 14.
_mknod:
        mov     r5,-(sp)
        mov     sp,r5
        mov     4(r5),0f
        mov     6(r5),0f+2
        mov     8(r5),0f+4
        sys     0; 9f
        bec     1f
        jmp     cerror
1:
        clr     r0
        mov     (sp)+,r5
        rts     pc
.data
9:
        sys     mknod; 0:..; ..; ..

monod.c側で、呼び出す手続きは、アセンブラでは、最初にアンダースコアが付いたものになる。 アセンブラ側では、フレームポインタとして使ってるr5をスタックに保存し、スタックの 先頭をフレームポインタにセットする。いわゆるプロローグの部分だ。

次に、C側の最初の引数から順番に保存してく。保存先は、0fってなってるけど、16進数と 解釈しちゃいけない。fは、これ以降(bの場合は以前)で、数字: と言うラベルって言う意味に なる。そう思って、0: を探すと、データエリアに保存してる事になる。

sysってのは、pdp11の命令表に出てこないんだけど、多分、トラップ命令の類だろう。で、 更に想像だけど、sys 0; 9fは、0;だったら、次のラベルである、9f、すなわち、データ エリアに作り上げた、引数付きのsys命令(すなわちシステムコール)を、実行してね。

データエリアのsysの後ろには、14と言うシステムコール番号、(C言語上の)第一引数、第二引数、第三引数 と言う事になるのか。 動的に命令を作り上げて、それを実行すると言う、昔よく使った懐かしい技法だ事。

sysから戻ってきた時C=1なら成功で、ラベル1: に飛ぶ。C=0なら、システムコール失敗で エラールーチンへ飛ぶんだな。後は、エピローグで、戻り値を0にして、フレームポインタを 戻してから、C言語側へと帰って行く。

ここにはエラールーチンのコードは無いけど、多分大域変数のerrnoに値をセットして、C言語用の エラーの返り値として負数をセットしてから、エピローグを実行して、C言語の世界へと戻って行く。 多分、こんな動きなんだろうな。

/ C return sequence which
/ sets errno, returns -1.

.globl  cerror
.comm   _errno,2

cerror:
        mov     r0,_errno
        mov     $-1,r0
        mov     r5,sp
        mov     (sp)+,r5
        rts     pc

やっぱり思った通りだった。それから、データ領域に命令を組み立てる方式のシステムコールを 間接システムコールと言うそうな。マニュアルは調べてみるものだ。

後は、Lions本のトラップのあたりを見ればいいのかな。遊んでばかりで、宿題やりなさいよ。