dd

debianの管理者ハンドブックを見てたら、lsXXXってのが有るのを知った。

sakae@debian:~$ ls
ls           lsblk        lsinitramfs  lsof         lsusb
lsattr       lscpu        lslocks      lspci
lsb_release  lsdev        lsmod        lspgpot

魅せるなんとか軍団だな。

これは、diskの分割か。

sakae@debian:~$ lsblk
NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
sda      8:0    0   15G  0 disk
├─sda1   8:1    0 14.3G  0 part /
├─sda2   8:2    0    1K  0 part
└─sda5   8:5    0  672M  0 part [SWAP]
sr0     11:0    1 56.5M  0 rom

次は、PCIバスにぶら下がっているデバイス群。

sakae@debian:~$ lspci
00:00.0 Host bridge: Intel Corporation 440FX - 82441FX PMC [Natoma] (rev 02)
00:01.0 ISA bridge: Intel Corporation 82371SB PIIX3 ISA [Natoma/Triton II]
00:01.1 IDE interface: Intel Corporation 82371AB/EB/MB PIIX4 IDE (rev 01)
00:02.0 VGA compatible controller: InnoTek Systemberatung GmbH VirtualBox Graphics Adapter
00:03.0 Ethernet controller: Intel Corporation 82540EM Gigabit Ethernet Controller (rev 02)
00:04.0 System peripheral: InnoTek Systemberatung GmbH VirtualBox Guest Service
00:05.0 Multimedia audio controller: Intel Corporation 82801AA AC'97 Audio Controller (rev 01)
00:06.0 USB controller: Apple Inc. KeyLargo/Intrepid USB
00:07.0 Bridge: Intel Corporation 82371AB/EB/MB PIIX4 ACPI (rev 08)
00:0b.0 USB controller: Intel Corporation 82801FB/FBM/FR/FW/FRW (ICH6 Family) USB2 EHCI Controller
00:0d.0 SATA controller: Intel Corporation 82801HM/HEM (ICH8M/ICH8M-E) SATA Controller [AHCI mode] (rev 02)

USBのハブは、リナ製とvbox製が付いているんだな。同じ事を、VMWAREでやったら、 どうなるんだろう。忘れずにやって見れ。

sakae@debian:~$ lsusb
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 002: ID 80ee:0021 VirtualBox USB Tablet
Bus 002 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub

VMWARE上のfedora23では、こうなった。

[sakae@fedora ~]$ lsblk
NAME            MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
sda               8:0    0   12G  0 disk
├─sda1            8:1    0  500M  0 part /boot
└─sda2            8:2    0 11.5G  0 part
  ├─fedora-root 253:0    0 10.3G  0 lvm  /
  └─fedora-swap 253:1    0  1.2G  0 lvm  [SWAP]
sr0              11:0    1 1024M  0 rom
[sakae@fedora ~]$ lsusb
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 003: ID 0e0f:0002 VMware, Inc. Virtual USB Hub
Bus 002 Device 002: ID 0e0f:0003 VMware, Inc. Virtual Mouse
Bus 002 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
[sakae@fedora ~]$ lspci
00:00.0 Host bridge: Intel Corporation 440BX/ZX/DX - 82443BX/ZX/DX Host bridge (rev 01)
00:01.0 PCI bridge: Intel Corporation 440BX/ZX/DX - 82443BX/ZX/DX AGP bridge (rev 01)
00:07.0 ISA bridge: Intel Corporation 82371AB/EB/MB PIIX4 ISA (rev 08)
00:07.1 IDE interface: Intel Corporation 82371AB/EB/MB PIIX4 IDE (rev 01)
00:07.3 Bridge: Intel Corporation 82371AB/EB/MB PIIX4 ACPI (rev 08)
00:07.7 System peripheral: VMware Virtual Machine Communication Interface (rev 10)
00:0f.0 VGA compatible controller: VMware SVGA II Adapter
00:10.0 SCSI storage controller: LSI Logic / Symbios Logic 53c1030 PCI-X Fusion-MPT Dual Ultra320 SCSI (rev 01)
00:11.0 PCI bridge: VMware PCI bridge (rev 02)
00:15.0 PCI bridge: VMware PCI Express Root Port (rev 01)
 :
00:18.7 PCI bridge: VMware PCI Express Root Port (rev 01)
02:00.0 USB controller: VMware USB1.1 UHCI Controller
02:01.0 Ethernet controller: Advanced Micro Devices, Inc. [AMD] 79c970 [PCnet32LANCE] (rev 10)
02:02.0 Multimedia audio controller: Ensoniq ES1371/ES1373 / Creative Labs CT2518 (rev 02)
02:03.0 USB controller: VMware USB2 EHCI Controller

OpenBSDでもpciを見られる。-vを付けると詳細が出てくる。面倒なら、dmesgかな。

# pcidump
Domain /dev/pci0:
 0:0:0: Intel 82443BX AGP
 0:1:0: Intel 82443BX AGP
 0:7:0: Intel 82371AB PIIX4 ISA
 0:7:1: Intel 82371AB IDE
 0:7:3: Intel 82371AB Power
 0:7:7: VMware VMCI
 0:15:0: VMware SVGA II
 0:16:0: BusLogic MultiMaster
 0:17:0: VMware PCI
 0:21:0: VMware PCIE
  :
 2:0:0: AMD 79c970 PCnet-PCI
 2:1:0: Ensoniq AudioPCI97

arm mmu

何度目かのARM MMU理解への挑戦。

ARMベース・システムLSI開発の事例研究

なるほど、コプロは15番で、そこにはレジスタが16本載ってるとな。それで、 キャッシュの制御やらMMUの制御をやってるとな。

実例が無いかと探してみたよ。

asp-gr_peach_gcc-mbed/arm.c

MMUを動かすのに特化したサンプルだな。そして、

rpi_start.S 上記は、NetBSDの入り口だったけど、Linuxでは、 /usr/src/linux-source-3.16/arch/arm/kernel/head.Sあたりが相当するらしい。

この2つのソースを見比べてみると、NetBSDは、綺麗、鮮やかな印象を受ける。 リナのそれは、ごちゃごちゃしてて、見る気が起きないな。

dd

とまあ、armを見たわけだけど、ソース読むなら、絶対にBSD系が良いと思うぞ。 素直だし、綺麗だと思う。(大事な事だから2度言いましたよ。世迷いでは ありません)

で、前回はdcだった。次は、ruby語のsuccを実行すると、ddになるんで、ddを OpenBSD上で読んでみる。リナみたいに、お取り寄せは必要無いし、勝手知ったる 我が家ですからね。

rubyを暫くやってなかったんで、上記いまいち自信が無い。調べたら、最新式rubyが OpenBSDにも有るそうなので、入れてみた。

# pkg_add ruby-2.3.1p1
quirks-2.241 signed on 2016-07-29T15:39:09Z
ruby-2.3.1p1:libyaml-0.1.6p1: ok
ruby-2.3.1p1: ok
--- +ruby-2.3.1p1 -------------------
If you want to use this package as your default system ruby, as root
create symbolic links like so (overwriting any previous default):
 ln -sf /usr/local/bin/ruby23 /usr/local/bin/ruby
 ln -sf /usr/local/bin/erb23 /usr/local/bin/erb
 ln -sf /usr/local/bin/irb23 /usr/local/bin/irb
 ln -sf /usr/local/bin/rdoc23 /usr/local/bin/rdoc
 ln -sf /usr/local/bin/ri23 /usr/local/bin/ri
 ln -sf /usr/local/bin/rake23 /usr/local/bin/rake
 ln -sf /usr/local/bin/gem23 /usr/local/bin/gem

yamlがお供にやってきた。名前を普通のにしとくといいよって事なんで、そうして おいてからREPLを起動。

[ob: ~]$ irb
irb(main):001:0> 'dc'.succ
=> "dd"

おお、正解だった。このsuccはpythonにも無い、日本文化の、お・も・て・な・し。

DESCRIPTION
     The dd utility copies the standard input to the standard output, applying
     any specified conversions.  Input data is read and written in 512-byte
     blocks.  If input reads are short, input from multiple reads are
     aggregated to form the output block.  When finished, dd displays the
     number of complete and partial input and output blocks and truncated
     input records to the standard error output.

こういう代物。512Byte単位でファイルをコピーするのが基本機能。 512Byte単位ってのは、Diskのセクターに書ける基本サイズを意識してのもの。 で、その時に引数を、 name=val 形式で与える事により、ただのcpでは真似が出来ない事を実現してる。

例えば、以前ちょっとやったけど、ファイルの頭から数ブロック飛ばしてコピー するとか、逆に、頭から指定したブロックだけをコピーするとか。 コピーする時に、コード変換を施しながらとか。ここで言うコードは、ASCIIコードと 大型コンピュータで採用されてるEBCDICコードの事。大文字、小文字変換も 出来るって、そこはかとなく昔の仕様を引きずっている感じがするな。

こんなddだけど、BSD系では基幹コマンドの扱いを受けていて、/binの下に 鎮座してる。ヘッダーファイルを含めて、8個で出来上がっていた。もとえ、 これに加えて、manの原稿とMakefileが必須。

今回は、ヘッダーファイルから見ていく。dd.hには、IOって構造体とSTATって 構造体が用意されてた。前者は、文字通り入力と出力の制御情報をまとめて ある。文字デバイスとかパイプとかテープとか読めないデバイスとか、面白い ものが混じっているぞ。

パイプは切捨て出来ないととかテープはseek出来ないよとか注意書きが有るぞ。 って、事は処理を分ける必要が有るんだなと、想像出来る。

STATの方は、文字通り統計情報を管理するまとまりになってましたよ。

それに続いて、各種のフラグビットが定義されてた。これをセットするのは 解析機関の仕事で、使うのは要所々に分散してるんだな。

もう一つ、extern.hが有った。こちらは、関数の型定義と、グローバル変数の 定義が載ってた。関数の引数が全て、voidって事は、全部の主要関数が、 グローバル変数を見て動いているのか。随分昔風の作りだな。

そもそも、EBCDICなんて言う文字コードが出てくる時点で、大型コンピュータと データのやり取りをするために作られたと想像出来る。manに歴史は載って いなかったので、勝手に歴史を調べてみる。

我がunix-v6にddは有ったか? dd page from Section 1 of the unix-6th manualこの通り、ちゃんと有りましたねぇ。

unixより先に大型コンピュータが有ったはずなんで、その頃のunixは、大型コンピュータ では、処理が高価で出来ないような事を、下請けしてたんでしょう。

The UNIX Treeなんて言う、unixの 歴史的ソースを集めた所を見ると、unix-v5の時代にddが登場してますな。 こういう歴史を背負って今のBSDとかが有るのね。

で、先のヘッダーを眺めていると、jclなんて言う意味不な関数が有った。これは何? 定義は、args.cなんて言う、名は体を表すって所に有った。

/*
 * args -- parse JCL syntax of dd.
 */
void
jcl(char **argv)
{
        struct arg *ap, tmp;
        char *arg;

        in.dbsz = out.dbsz = 512;
         :

JCLを用語事典で引いてみた。 JCLとは|ジョブ制御言語|Job Control Language IT用語辞典

大型コンピュータでプログラムを起動する時の仕様書って事ですかね。 (ついでに、 EBCDICも) そう言えば、このddに与える引数は、unixの流儀に沿っていない。 matzさんが渇望してた、キーワード引数方式で与える事になっている。 例えば、こんな具合にね。

[ob: ~]$ dd if=miniroot-cubie-60.fs of=zzz skip=6144

これだけ前提知識が有れば良いだろう。

ああ、512って暗黙知の数字が、難の説明も無く使われているな。あの人が眼の仇に しそう。他に無いか探ってみたぞ。

[ob: dd]$ grep -n 512 *.[ch]
args.c:96:      in.dbsz = out.dbsz = 512;
args.c:313: *           b - multiply by 512.
args.c:339:             num *= 512;
args.c:396: *           b - multiply by 512.
args.c:418:             num *= 512;

いにしえのコードでも、そのまま出てくるから、正に暗黙知。知らない人はこの 業界のもぐりです。

いにしえのコードの方が優しそうなので、そちらを読んでみるか。

512は、ibsとobsとブロック数を掛け算する、3箇所に出てくる。ibsとobsには、 代入が行われていないので、定数的な使い方をしてる。

なんて言うのは、嘘でーす! と、いもとの2世風(なんのこっちゃ)

        if (bs) {
                ibs = obs = bs;
                if (conv == null)
                        fflag++;
        }

bsが優先する仕様なのね。それから、カーニハン&リッチーのスタイルでソースが書かれて いるな。こういうの、ほっとするよ。

でも、ほっとするのは、ここまで。見事と言う程、コメントが書かれていない。 それに、関数の定義の順番が普通はmainが最後に来るのに、一番頭に書かれている。 先のjclなんて名前と言い、これは非unix文化の人が書いたに違いないと、現代の 考古学者は推測しました。

きっと、大型コンピュータをやってた人が、unixを下請けに使う為、書いたんだろうな。 だから、突然EBCDICとのコード変換機能が入っていたりする。

現代OS上で再現させる

絶滅したシベリアの冷凍マンモスからDNAを抽出して、マンモスを再現する計画が進行してるとか。 ならば、ソースからバイナリーを再現してやろう。舞台はOpenBSD。

まず、そのままコンパイルしてみる

[ob: ~]$ cc dd.c |& grep error
dd.c:15: error: expected '=', ',', ';', 'asm' or '__attribute__' before numeric constant
  :
dd.c:375: error: 'obs' undeclared (first use in this function)
dd.c:386: error: expected expression before '|' token
dd.c:418: error: 'atoe' undeclared (first use in this function)
dd.c:442: error: too few arguments to function 'exit'

警告は、数え切れないぐらい出て来るので、エラーだけ拾ってみた。全部で56個 有ったよ。楽しみながらエラーを潰していく。昔の文法は、そーなってたのねと 分かって面白かったぞ。

現代のエキスは、#includeでstdio.hを注入しておいた。昔はどうやってたのか 後で調べておけ。多分makeも無い時代だろうから、shellスクリプトを駆使して コンパイルしてたんだろうね。

warningが26個程出てきたけど、無事にコンパイル終了。走らせてみる。

[ob: tmp]$ dd if=ndd.c of=z
24+1 records in
24+1 records out
12308 bytes transferred in 0.000 secs (13907345 bytes/sec)
[ob: tmp]$ ./a.out if=ndd.c of=y
24+1 records in
24+1 records out
[ob: tmp]$ cmp z y

オリジナルのddと結果が一致したんで、まあ成功ですかね。

昔のmakeを調べてみた。やっぱりそんなの無くて、runって名前に統一された シェルスクリプトが用意されてた。

# chdir usr/source/s1
# ed run
3286
/dd.c/
cc -s -O dd.c
.,.+5p
cc -s -O dd.c
cmp a.out /bin/dd
cp a.out /bin/dd

cc -s -O df.c
cmp a.out /bin/df

これを見ると、どうもテスト用って感じだな。コンパイルしてバイナリーが生成され それと元からあるやつを比較なんて事をやってる。

コンパイラーのフラグ、Oはオプチィマイザー。sはリンカーのフラグ。

     -s  `squash' the output, that is, remove the symbol table
         and relocation bits to save space (but impair the use-
         fulness of the debugger).  This information can also be
         removed by strip.

この時代にもdebuggerは有ったんだな。マニュアルをザット見した限りでは、 coreの検視用機能しかサポートしていない。本来のdebugは、print文を 埋めたバイナリーを走らせて実施してたんだな。

それでdebug出来ないようなcoreしちゃうような時、このプログラムを 使うって算段なんだ。

あっ、cdbなんてのも有るな。こちらは、BPを置いて、途中でプログラムを止める 機能が付いてるよ。systemcallのあの機能を使っているんだな。 また、v6の本を読み直してみるかな。

前回のdcがらみ、古いmanを見ると、あのkenさんの署名が有った。 作成日を見ると、1971/11/3 になってる。日本では文化の日。こういう日に 格調高いプログラムをリリースするって、さすがkenさんだ。

とか言っていたら、kenさんのいたずらを発見。 考えさせられるなあ。

diff of dd

下記は、主な変更パターン。

+#include <stdio.h>

-char    *ibs    512;
+char    *ibs  =  512;

-char    etoa[]
+char    etoa[] =

-                n =* 1024;
+                n *= 1024;

-        exit();
+        exit(1);

-        printf("%l+%l records in\n", nifr, nipr);
+        printf("%d+%d records in\n", nofr, nopr);

-                                cflag =| UCASE;
+                                cflag |= UCASE;

-                        for(ip=ibuf+ibs; ip>ibuf;)
+                        for(ip=ibuf + (int)ibs; ip > ibuf;)

-                c = (ibc>>1) & ~1;
+                c = ((int)ibc >> 1) & ~1;