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本のトラップのあたりを見ればいいのかな。遊んでばかりで、宿題やりなさいよ。