📝

TIL: UNIXプログラミング環境 第2章

に公開
2

UNIXプログラミング環境 第2章

今回から3節ごとを目処に記事にします。
内容はToday I Learnedということで、学びを記す記事です。
 詳しいわけではないので悪しからず。
書籍はこちら
UNIXプログラミング環境

ファイルシステム

2.1 ファイルの基礎

  • ファイルとはバイトの集合。
  • ファイルはなんの制約も受けず、その内容に意味を持たされることはない
  • プログラムが解釈して初めて意味を持つ

これは周辺機器にも当てはまり、磁気テープからメール、パイプのような流れるデータもバイトの集合でしかない。

ファイルをいじるのが最も勉強になるのことで、プロンプトの操作に入る。

$ ed
a
now is the time
for all good people
.
w junk
36
q
$ ls -l
-rw-r--r-- 1 ~~~(省略)

od (octal dup : 8進数ダンプ)を使うと、バイトを全て目で見える形にできる。
-c オプションがバイトを文字と解釈せよという意味である。

$ od -c junk
0000000    n   o   w       i   s       t   h   e       t   i   m   e  \n
0000020    f   o   r       a   l   l       g   o   o   d       p   e   o
0000040    p   l   e  \n                                                
0000044

-b オプションで8進数を表示できる。

$ od -b junk
0000000   156 157 167 040 151 163 040 164 150 145 040 164 151 155 145 012
0000020   146 157 162 040 141 154 154 040 147 157 157 144 040 160 145 157
0000040   160 154 145 012                                                
0000044

空白のバイト数が040と知りました。
 ここで左端の7桁の数字は、そのファイルの中の位置、つまり、次に表示される文字が何番目に当たるかを8進数で示している。

8進数は昔のシステムの名残で、16進数の方が相性の良いシステムが増えていきている。

制御文字 (\nなど)も特殊に見えて、8進数で表示すればただの数値であるとわかる。

制御文字 対応する8進数の数値
改行 \n 012
タブ \t 011
バックスペース \b 010
キャリッジリターン \r 015

2.2 ファイルの種類

ファイルのフォーマットは、そのファイルを使うプログラムが決める。
 ファイル・システムが決めるわけではなく、プログラムがファイルの種類を決めるため、カーネルはファイルの種類を認知していない。
そこでfileコマンドが役にたつ。fileコマンドは拡張子を確認してfileの種類を決めるのではなく、先頭の数百バイトを読み、ファイルの種類を決める鍵となるものを探す。

実行可能形式のプログラムはわかりやすく、先頭にバイナリの"マジックナンバー"がついている。
16進数で表示する hexdumpを使用すると (max os を使用しています)

$ hexdump /bin/ed
0000000 feca beba 0000 0200 0001 0700 0000 0300
0000010 0000 0040 0100 2097 0000 0e00 0001 0c00
0000020 0080 0200 0200 0000 0100 2097 0000 0e00
0000030 0000 0000 0000 0000 0000 0000 0000 0000
....

先頭4bytesがマジックナンバーだそうなので、feca bebaがマジックナンバーだと推定できる。
 大きい数値になっていることが信憑性を担保していて、エディターでタイピングできない値がマジックナンバーとなる。 => ASCIIコードで入力できる値はマジックナンバーではないため、非常に大きな値が設定されているので断定できる。

他にも最初が #includeで始まっていればcファイルだと判断するし、.で始まっていれば nroffやtroffの入力ファイルと判断する。

テキストファイルのみで構成されることの利点などは書籍をご覧ください。かなり詳細に書かれていました。

2.3 ディレクトリとファイル名

全てのファイルには絶対パスとして /user/bin/(省略)といったファイルの名前を持っているが、lsコマンドを実行すると、junkのようなファイル名だけが出力される。
 lsはカレント・ディレクトリを持っていて、その中だけをlist upするためである。
 現在のカレント・ディレクトリは、 pwd (print working directory)で表示できる。

$ pwd
(がっつり本名が出たので省略)

ここでカレント・ディレクトリはプロセスにつけられた属性であり、ユーザやプログラムには属していない。
 もし親プロセスが子供のプロセスを生み出せば、子供プロセスは親のプロセスのカレント・ディレクトリを受け継ぐ。ただ子供のプロセスに何をしても、親プロセスに影響を与えない。
 これらの機能は当然、ファイルの組織化、階層化のためである。

$ mkdir recipes
$ cd recipes
$ pwd
(省略)
$ mkdir pie cookie
$ ed pie/apple
(省略)
$ ed cookie/choc.chip
(省略)

この例のように、pieのレシピが一箇所に集まった方が良いだろうということである。

du (disk usage)コマンドで、階層構造のうちどのファイルがどれくらいディスクを使用しているか閲覧できる。

$ cd recipes
$ du -a
8	./pie/apple
8	./pie
8	./cookie/choc.chip
8	./cookie
16	.

-aオプションはallを意味する。
 左の数字がファイルの保存に使用されているブロック数であり、1ブロックあたり、512バイトまたは 1024バイトである。


ファイルの探索Tips

$ du -a |grep (検索したいファイル名)
(結果)

ずっとお前を探していたんだ、、、


話は戻って、ディレクトリは通常のファイルと異なった性質を持つが、ファイルシステムの中ではファイルと同じような形で入っている。つまり、前述のodコマンドで指定するとバイトが見える。(驚き)

$ od -cb .

ウキウキして実行しましたが、Macではできませんでした。
Linuxで試したいです。

まとめ

今日はここまで!

  • ファイルとはバイトの集合
  • 種類を決めているのはプログラムで、本来ファイルは自由なもの
  • ディレクトリもファイルと大差はない、ファイルの位置を示すためのファイルというイメージ

今までなんとなくで済ましていた部分がUNIXを用いて基本的な構成を学ぶことで、全体が見えてきて、そのおかげで別の場所で浮かんでいた疑問が解決するシーンがありました。(具体例は浮かんでは消えてを繰り返したので割愛)

1章の記事に体系的に学ぶと書きましたが、今まで触ったことのあるコマンドばかりで新たな発見がなかった中、体系的に学ぶことの意義、効果が早速現れてモチベーションになりました。

2書の続きはこちら

Discussion

ko1nksmko1nksm

$ od -cb .

Linuxでも、おそらく現在のどの Unix でも見れないと思います。
UNIXプログラミング環境は原著が1983年の本なので、
40年前の内容であることに注意してください。
一部の内容は時代遅れになっています。

ko1nksmko1nksm

NetBSD 10では見れました。

vm-netbsd10-0$ ls -al /libexec
total 752
drwxr-xr-x   5 root  wheel     512 Apr 16 02:32 .
drwxr-xr-x  21 root  wheel     512 Mar 31  2024 ..
-r-xr-xr-x   1 root  wheel    5980 Mar 28  2024 blocklistd-helper
drwxr-xr-x   2 root  wheel     512 Mar 28  2024 devpubd-hooks
-r-xr-xr-x   1 root  wheel     361 Mar 28  2024 devpubd-run-hooks
drwxr-xr-x   2 root  wheel     512 Mar 28  2024 dhcpcd-hooks
-r-xr-xr-x   1 root  wheel    8133 Mar 28  2024 dhcpcd-run-hooks
-r-xr-xr-x   1 root  wheel  141360 Mar 28  2024 ld.elf_so
-r-xr-xr-x   1 root  wheel  136768 Mar 28  2024 ld.elf_so-i386
-r-xr-xr-x   1 root  wheel   75784 Mar 28  2024 lfs_cleanerd
drwxr-xr-x   3 root  wheel     512 Mar 28  2024 resolvconf
vm-netbsd10-0$ hexdump -C /libexec
00000000  00 62 01 00 0c 00 04 01  2e 00 00 00 02 00 00 00  |.b..............|
00000010  0c 00 04 02 2e 2e 00 00  01 62 01 00 1c 00 08 11  |.........b......|
00000020  62 6c 6f 63 6b 6c 69 73  74 64 2d 68 65 6c 70 65  |blocklistd-helpe|
00000030  72 00 00 00 02 62 01 00  1c 00 08 11 64 65 76 70  |r....b......devp|
00000040  75 62 64 2d 72 75 6e 2d  68 6f 6f 6b 73 00 00 00  |ubd-run-hooks...|
00000050  03 62 01 00 1c 00 08 10  64 68 63 70 63 64 2d 72  |.b......dhcpcd-r|
00000060  75 6e 2d 68 6f 6f 6b 73  00 00 00 00 04 62 01 00  |un-hooks.....b..|
00000070  14 00 08 09 6c 64 2e 65  6c 66 5f 73 6f 00 00 00  |....ld.elf_so...|
00000080  05 62 01 00 18 00 08 0e  6c 64 2e 65 6c 66 5f 73  |.b......ld.elf_s|
00000090  6f 2d 69 33 38 36 00 00  06 62 01 00 18 00 08 0c  |o-i386...b......|
000000a0  6c 66 73 5f 63 6c 65 61  6e 65 72 64 00 00 00 00  |lfs_cleanerd....|
000000b0  07 62 01 00 18 00 04 0d  64 65 76 70 75 62 64 2d  |.b......devpubd-|
000000c0  68 6f 6f 6b 73 00 00 00  0a 62 01 00 18 00 04 0c  |hooks....b......|
000000d0  64 68 63 70 63 64 2d 68  6f 6f 6b 73 00 00 00 00  |dhcpcd-hooks....|
000000e0  0f 62 01 00 20 01 04 0a  72 65 73 6f 6c 76 63 6f  |.b.. ...resolvco|
000000f0  6e 66 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |nf..............|
00000100  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000200

こういう構造らしいです。

struct dirent {
    ino_t d_fileno;     // inode番号
    uint16_t d_reclen;  // レコードの長さ
    uint8_t d_type;     // ファイルの種類(DT_REG, DT_DIRなど)
    uint8_t d_namlen;   // 名前の長さ
    char d_name[NAME_MAX + 1]; // ファイル名(NUL終端)
};