Section Next | Prev | Index | 凡例


Linux スタートアップ 番外編 - 失われた技術

UNIX がまだ研究対象として、無償で配布されていた頃にあった機能のうち、 商用目的では意味を成さないと判断されたものは実装の対象からはずされています。 しかし名称や慣例として残っている物が多く、そのいわれをまとめてみました。

*例によって、当時の作者の思い違いがあるかもしれないので 鵜呑みにしないように


スクリプトの指定(#!)
シェルスクリプトの先頭には #! というコメントとも、コマンドともとれそうな 決まり文句があります。このきっかけは、過去に初めて csh が登場したときに遡れ ます。
当時のシェルには、どのシェルプログラムでスクリプトを実行するか判定機能が ありませんでした。csh のユーザが増えるに従い、従来からあったスクリプトが うまく動作しないという問題が表面化しました。
単に実行権を付けたスクリプトファイルを起動すると、その解釈はその時に使われて いたシェル(多くはログインシェル)に任せられていたためです。

そこでファイルの最初の一文字目がコメント(#)の場合は、csh とし、 何も実行しない命令 : コロン、NOP(No Operation) は bsh とするよう csh に修正が加えられました。
言い替えると得に指定しない場合は bsh を正とする運用となりました。

さらにシェルのバリエーションが増えてきたため、コメントを拡張し #! に続けて 実行すべきシェルプログラムのパス名を指定するという現在の仕様になりました。

なお、コメント(#)とNOP(:)は見た目は何も実行されませんが、コメントが読み飛ばさ れるのに対し、NOPは構文解析を行うため若干オーバーヘッドが発生します。

シェル(a)#の結果(b):の結果(b)/(a)備考
ash(bsh)0.32s4.06s12.68 (a)はコメント(#)だけを100万行用意したスクリプト。
(b)はNOP(:)を同じく用意したスクリプト。
各シェルで time を使い時間を測定、それを5回繰り返した平均を採用。
$ time ash cm1
bash2.89s21.90s7.57
ksh1.80s6.76s3.75
tcsh9.37s23.26s2.48

GECOS領域
chfn(1)を用いると、passwd(5) に住所や電話番号を記述することができます。 実際には passwd(5)内にカンマで区切って入力した文字列が羅列しているだけですが、 ここの事を古い UNIX では GECOS領域と呼んでいます。
これは
K.T, D.R といった AT&T Bell Lab. の古参の研究者が UNIX で作成した データを GE のマシン上で印刷するために必要となるアカウント情報を書き留めて いたことに由来します。
つまり Bell Lab. でまだGEのマシンを利用していたころの名残という事になります。

Hello, world
なぜ、プログラミングの例題に "Hello, world!" と表示する例が多いのか? よく聞かれる事がありますが、これは UNIX のネイティブ言語であるCの入門書に 由来します。
Cは Verion 2 ぐらいで、アセンブラからC言語で再コーディングされましたが、 開発した K&R (カーニハンとリッチー)が自ら著した入門書「プログラミング言語C」 の一番最初の例題として採用したのが "Hello, world!" と標準出力に印刷する プログラムでした。 今でも UNIX を基盤としたプログラミング言語の例題として、採用されている事を 考えると、非常に多くの研究者、技術者がこの入門書に目を通していたと思われます。

なぜコマンド名が極端に短いのか?
Ken Tompson はタイプが非常に苦手だったようで、実装初期のコマンドは多くが2文字、 中には1文字といったコマンドが存在します。ファイルを作成するシステムコールに至っては creat(2) で、作者自身も後日 UNIX を作り直すとしたら?という質問に対し creat の 綴りを create にしたいと言ったという逸話があるほどです。
また黎明期の UNIX ファイルシステムでは、ファイル名長が数バイトしかなかったという 技術的な制限があったことはいうまでもありません。

Sticky bit
ディレクトリの属性であるスティッキービットは、そこにあるファイルの内容が 変更可能であっても、ファイル自体を削除できるのは作者本人のみという制限を与えます。 わかったようなわからないような説明ですが、すくなくともなぜ「張り付く:スティッキー」 なのか?そのいわれについてよく質問を受けます。
そもそもスティッキービットは仮想記憶が実装された頃に、このフラグたついた実行ファイル が終了しても仮想メモリ(セグメント、ページ)に保存しておき、次回起動要求があった ときにファイルシステムから読み込むのではなく、仮想メモリから直接実行できるよう 指示するために用いられていました。 だからプログラムが仮想記憶空間に張り付いたままに見えるため、スティッキービット と呼ばれていました。いまは潤沢にメモリーもあり、ディスクも高速なので用途が変わって きたということです。

fork(2)
伝統的なUNIXでは、プロセスを生成する再に新規にプロセスコンテキストやヒープリスト、 リソースのリストを生成するといった「新しくプロセスを作る」といった作業はしません。 唯一生成されるのが init プロセスで、それ以降、すべてのプロセスは彼のコピーとして 作成されその後調整を受ける形となります。
多くのOSの場合、新しくプロセスを作るというのは「生みの苦しみ」であり、かなりの 手間を要します。ところが UNIX はこれを回避するために、既存のプロセスをコピーする という手法を用いました。絵にすると、プロセスが細胞分裂のように増えていくように 見えることから、プロセスの生成を fork(フォークのように二股、三股にプロセスが増える) と呼んでいます。

つまり伝統的な UNIX では、利用できるメモリの過半数を使用するようなプロセスが、 分裂しようとすると、メモリ不足で分裂できないとう致命的な欠陥がありました。 現在では、ヒープ領域やテキストエリア(プログラムそのもののイメージ)は複写せずに 新しいプロセスを生む方法や、そもそもプロセスではなくその中でプロセスコンテキストや CPUリソースだけを複写するといった、スレッドの概念を導入することで性能を上げて います。

マイクロカーネル
UNIX および Linux は古いタイプのOSだといわれます。 その理由でもっとも多い意見は「モノリシック(一枚岩)」タイプという事です。 Linux 黎明期に、MINIX の作者から指摘されたのも、この話題でした。
モノリシックの反対は、マイクロカーネルがあります。 OSの機能を極限まで絞って、ネットワークやI/Oなどをサブシステム化する、 カーネル自体を小型化するというのがマイクロカーネルの概念でした。

実際にOSが持つべき機能と、サブシステムやAPIが持つべき機能をどう分割するか? という議論はコンピュータ創造期から存在しています。 FORTRAN-III はモニターと呼ばれる必要最小限のBIOS相当の機能を持っていましたし、 MULTICS は PL/1 とあいまって、何でも万能にとりこうもと肥大化しました。 それをうけて UNIX(当時は UNICS) は必要最小限、Simple is best で発展しましたが、 BSD でネットワーク部分をカーネルに取込んでしまい、 その後 NFS が一般化されると多機能なカーネルの問題が露呈し、 より小さなカーネルが求められていました。
その頃から開発が始まった
GNU Hurd OS が、30年近くたってもなお、正式リリースされている無いことから、非常に難しい問題だと言えます。

cp の -r/-R オプションについて
cp の再帰的コピーオプション(指定したディレクトリ以下をごっそり)は、 最初の実装では -r のみでした。この実装はファイルをオープンし、内容を読み取り 同じ内容を指定された出力先に出力するものでした。
一般ファイルであば問題なかったのですが、デバイスファイルの場合はそのデータ 全てをよみとる形となります。たとえば /dev/zero では無限に 0(バイナリ) の データを出力し続ける事になり、コピーは終了しません。 そこで -R はデイバスの内容ではなくデバイスファイル だけをコピーするオプションとして定義されていました。 最近の実装では GNU オプションとして明確に指定しないかぎり、デバイスの内容 ではなくデバイスファイルをコピーするという実装となり、事実上 -r と -R の区別は 失われました。

rm の -r オプションについて
rm の再帰的オプション(指定したディレクトリ以下をごっそり)は、 危険で、特にスーパーユーザーで "# rm -rf / " なんてしたら、全てを削除。
と、本当に昔は大変な事になりました。新人SEがこなれて来て、マウスでコピペ。 手元が狂って、全てを削除するなんて事は、数年に一度はありました。
今は流石に安全装置が追加されて、本当に全てを削除するには "-–no-preserve-root" が必要となっています。

Linux の最大値
一度にオープンできるファイル数、所属可能なグループの最大値など、 Linux で扱う種々の最大値はヘッダーファイルとして登録されています。 またC言語の sizeof 演算子を用いて、データの物理的な大きさ(Bytes)を得る事が できます。
以下の例では、所属グループの最大値と、UID(uid_t)の大きさを表示しています。
	$ cat max.c
	#include <sys/types.h>
	#include <unistd.h>
	main()
	{
		printf("Max groups:%d\n", sysconf(_SC_NGROUPS_MAX) );
		printf("UID size:%d\n", sizeof(uid_t) );
	}

	$ make max
	cc     max.c   -o max
	$ ./max
	Max groups:65536
	UID size:4

Section Next | Prev | Index | 凡例