UNIX を讃えよ V7 を崇めよ The Last True UNIX, Seventh Edition(V7 UNIX)

久しぶりに UNIX の 1/4 世紀 引っ張り出して流し読み、リアルタイマーではない俺が当時の空気を感じるには必読書である。

後に Research UNIX と呼ばれるベル研発の UNIX は事実上これが最後のバージョンである。 数年の休止を経た後、V7 の VAX 移植版である 32V4.1cBSD をベースとした V8 として再起動し V10 まで開発が続くのだが、一般公開されなきゃ存在しないも同然でありここ最近突然に UNIX Archive で公開されようが知るかバカ!アホ!でしかねえのだ。

そもそも何故にこれが最後の一般公開となったかなのだが、それはベル研を養ってた AT&T そして PDP-11 バブルに沸く DEC にとって UNIX の想定外の反響によって「大きなシノギの匂いがするな…!」と心の白竜が疼いてしまったからに他ならない。

そもそも UNIX は

宣伝なし
サポートなし
バグ修正なし
前払い

がポリシーだったし、AT&T の背広組も価値があると思ってなく 石田 晴久/UNIX:UNIX システムの歴史と最近の動向 - 情報処理,27(12), (1986-12-15) によれば AT&T は大学には当初は $150 後に $1,200 と実質タダでソース配ってたのだ(企業でもせいぜい数万ドルである)。

しかしこの時 DEC は動いた、V7 リリースの同年 UEG(UNIX Engineering Group) を結成し UNIX/V7M (M=modified の意味)として商用サポートを開始したのである。

これは 4.2BSD ベースの Ultrix そして OSF/1 を経て Tru64 UNIX まで続くわけで、COMPAQ に吸収された DEC そして HP に吸収された COMPAQ より長生きしたのだ、つーかブーン分社化した HPE が Tru64 UNIXのドキュメントを全消ししやがったの許せねえなオイ!?

日本 DEC の清兼・末広両氏による 国際化プログラミング I18N ハンドブック の元となった Tru64 UNIX 国際化ソフトウェア・プログラミング・ガイドがもうネット検索してもかすりもしないヒデえ時代なのである、まぁ Tru64 UNIX のサポート終焉が 2012 年だから仕方ねえか。

DEC は当初は UNIX を競合とみなしてたのだが PDP-11 のセールスの強力な原動力なんだしそりゃ手首のモーター高速回転させてプロモーションに力をいれるってもんよ。 まるで 食器乾燥機 がプラモデル売り場に置いてあるような話だな?

そして Microsoft が 1980 年に 8086 に V7 を移植し XENIX として発売する。 これは後に事業譲渡され SCO OpenServer となる。

また Unisoft が 1981 年に V7 を m68000 に移植しこれが SunOS や AU/X そして IRIX の礎となる。

ん?IBM?こいつら UNIX に何も貢献してねえよなって言われててダメだった。 ビッグブルーが存在感を醸すのはずいぶん遅くて OSF/1 への合流から、そして一番最初に UNIX に見切りをつけ Linux へ乗り換え SCO に噛みつかれる事になるのだがそれはまた先の話。でも AIX は維持しててえらい。

これらの動きに PWB/UNIX という開発環境を細々と売ってた AT&T は他社が自分達以上に UNIX で商売に成功ているのを目の当たりにして

純白のメルセデス
プール付きのマンション
最高の女とベッドでドンペリニヨン
欲しいものは全てブラウン管の中

と嫌儲精神に狂い Research UNIX をクローズドソースとし商用の System III をリリースし、さらに System V へ発展し UNIX Systems Laboratories(USL) を設立する。

こうしてはじまった UNIX 戦争 は System V Release 4(SVR4) と BSD との 兜合わせ 鍔迫り合いとなり X/Open Portability Guide(XPG) のような協調もあったものの結局は USL vs BSDi 訴訟へと発展していくのだがそれはまた先の話。

末路はあっけないものである、USL の知財は Novell そして SCO/Caldera へと転々とし 2015 年に System V Release 5(SVR5) を僭称する UnixWare 7/OpenServer 6 を最後に血脈は絶たれたのである(大河ドラマ)。

V7 における改善点

まあいいや本題に戻る、V6 から V7 はカーネルのコードが 80% 増しだそうである。 さぞや現在時刻まわりのコードも進歩したんだろうなと思った?

残念でした。

  • long int が導入されて 32bit integer が扱えるようになり int time[2] である必要が無くなった
  • typedef が導入されたことで <sys/types.h>time_t が導入された

くらいしかねえ!いやマジで。

4 typedef	long       	time_t;   	/* a time */
10 int	lbolt;			/* time of day in 60th not in time */
11 time_t	time;			/* time in sec from 1970 */
28 clock(dev, sp, r1, nps, r0, pc, ps)
29 dev_t dev;
30 caddr_t pc;
31 {
...
37 	/*
38 	 * restart clock
39 	 */
40 
41 	lks->r[0] = 0115;
...
87 	/*
88 	 * lightning bolt time-out
89 	 * and time of day
90 	 */
91 out:
...
109 	if(++lbolt >= HZ) {
...
112 		lbolt -= HZ;
113 		++time;
...
137 	}
138 }

いみじくも Rob Pike が UNIX の賞味期限は 1970 年代にとっくに切れてるというわけである(発言の時代が違う)。

なのでベル研の新たな旅に追従して、次回からは我らの約束された未来 Plan9 編がはじまる

わけねえだろ

いやちゃんと V7 でも進捗あるってば、じゃーはじめんぞじゃー出席とるぞー(null)。

timeb.h と ftime(2) システムコールの登場

1 /*
2  * Structure returned by ftime system call
3  */
4 struct timeb {
5 	time_t	time;
6 	unsigned short millitm;
7 	short	timezone;
8 	short	dstflag;
9 };
21 #define	HZ	60		/* Ticks/second of the clock */
22 #define	TIMEZONE (5*60)		/* Minutes westward from Greenwich */
23 #define	DSTFLAG	1		/* Daylight Saving Time applies in this locality */
22 /*
23  * New time entry-- return TOD with milliseconds, timezone,
24  * DST flag
25  */
26 ftime()
27 {
28 	register struct a {
29 		struct	timeb	*tp;
30 	} *uap;
31 	struct timeb t;
32 	register unsigned ms;
33 
34 	uap = (struct a *)u.u_ap;
35 	spl7();
36 	t.time = time;
37 	ms = lbolt;
38 	spl0();
39 	if (ms > HZ) {
40 		ms -= HZ;
41 		t.time++;
42 	}
43 	t.millitm = (1000*ms)/HZ;
44 	t.timezone = TIMEZONE;
45 	t.dstflag = DSTFLAG;
46 	if (copyout((caddr_t)&t, (caddr_t)uap->tp, sizeof(t)) < 0)
47 		u.u_error = EFAULT;
48 }
 53 int	ftime();
...
 65 struct sysent sysent[64] =
 66 {
...
102 	1, 0, ftime,			/* 35 = ftime; formerly sleep */
...
131 };

突然どうした!クッソいい加減な時刻管理しかやってなかった Research UNIX が突然

  • millitm … ミリ秒単位の時刻
  • timezone … 現在のタイムゾーン
  • dstflag … 夏時間

なんて返すシステムコールを載せてきやがったぞ!?

ヘッダを新設するほど複雑なオブジェクトを返すシステムコールというのも当時では珍しいといえる。

まぁ実装自体はご覧の通りたいして変化なく分解能はたったの 1/60 秒でありミリ秒なんて精度は無いし、タイムゾーンと夏時間に至ってはハードコードである。

実はタイムゾーンと夏時間についてすでに V6 UNIX の時点で libc に gmtime(3)localtime(3)実装済 だったのだけど、この V7 では

  • 現在のタイムゾーン
  • 夏時間が有効かどうか

の情報を libc からカーネルに移動したのだ、V6 では libc で持ってたんだよねこれ。

58 int timezone	5*60*60;
...
64 int	daylight 1;	/* Allow daylight conversion */

ん?カーネルでタイムゾーンと夏時間?ちょっとおかしくねえか?

勘のいい人はもうお気づきであろう、ド派手におかしいし完全なる設計ミスだゾ。

すでに多くの計算機がネットワークすなわち ARPANET へ繋がれはじめている時代である。 別の地域に住むユーザーが電話回線で遠隔地にある計算機へログインなんて一部の人間ではあれど当たり前に行われてるんですよな。

それなのにこの実装のようにタイムゾーンと夏時間をカーネルで管理したら、計算機の置かれてる場所のタイムゾーンと夏時間をユーザー全員が強制されてしまうアホな事態が発生する、V6 時点の実装で正解だったんですよこれ。

現代の OS においてタイムゾーンと夏時間の管理は libc の中で完結している。 そして内容について IANA が管理する tz というデータベースを元にしているはずである。

余談だけどこの tz は 1970 年以前のデータの扱いが元で global-tz なんて fork が爆誕して N とか配布元こっち切換えてんだな。

内容が不正確な上に UNIX で扱う必要のない 1970 年以前とかどうでもいいのにミーハーなことで。 そもそもこの主がこの先何十年もメンテしていけるのか考えたことあるのだろうか。

fork に至った経緯に興味ある人は これ とか これ そして fork のお知らせ あたりからスレッド追ってけばいいと思う。

話を戻すと、結論としては最初に書いた通り設計ミスである。 よって POSIX:2001 で ftime(2) は deprecated とされ、最新版では抹消されベル研の黒歴史はひっそり葬られた。

そもそも何がやりたかったの

ネットワーク対応のためにミリ秒単位で現在時刻の取得がしたかったんだ。 それにしてはネットワーク対応に致命的な前述の設計ミスがあってクッソ笑うんだけど。

元々この ftime(2) は UNIX 最初のネットワーク実装の試みである SRI-NOSC Unix 由来と思われる。

こいつは V6 UNIX に ARPANET で当初使われてた Network Control Program(NCP) を実装したものなのだ。

26 /* no high-resolution timing needed....
27 #define FASTIME		/* maintain line-frequency timer */
 16 int	sysent[]
 17 {
...
 89 #ifndef FASTIME
 90 	0, &nosys,			/* 55 = x */
 91 #endif FASTIME
 92 #ifdef FASTIME
 93 	0, &ftime,			/* 55 = ftime */
 94 #endif
...
110 };
  8 #ifdef FASTIME
  9 extern int fastime;
 10 #endif FASTIME
...
 28 clock(dev, sp, r1, nps, r0, pc, ps)
 29 {
...
 38 	*lks = 0115;
 39 
 40 #ifdef FASTIME
 41 	fastime++;
 42 #endif FASTIME
...
117 	if(++lbolt >= HZ) {
...
120 		lbolt =- HZ;
121 		if(++time[1] == 0)
122 			++time[0];
...
173 	}
174 }
1 /	C library ftime -- high-resolution real-time clock for timing things
2 
3 	.globl	_ftime
4 _ftime:
5 	sys	60.	/ invoke kernel function -- returns value in r0
6 	rts	pc	/ so that's all

なぜか ftime(2) の実装がカーネルに無いのだが sysent[] にも libc にもあるし 何かの手違いで ken/sys4.c あたりを変更前のファイルで上書きしてしまったかと思われる。 まぁどういう実装化は容易に想像できるからいいか。

ご覧の通り timeb.h は存在しないしタイムゾーンや夏時間は関与せずに秒より高精度の時刻を返しているだけから V7 UNIX が単純にマージしたわけではではないことに注意。 つまりイリノイ大学は無罪であってただひたすらにベル研の失態なのである。

そして NCP の次世代である TCP/IP をやはり V6 UNIX に実装した BBN-V6 では秒単位の UNIX 時間に加えてクロック割込の 1/60 秒単位を返す qtime(2) が追加されている。

30 /* return time in 60ths of a second   ( BBN:mek  1/11/78 ) */
31 qtime()
32 {
33 	gtime();
34 	u.u_ar0[R2] = lbolt;
35 }
 13 int	sysent[]
 14 {
...
103 	0, &qtime,			/* 67 = qtime (BBN:mek  1/11/78) */
...
185 };

他にも fastime という変数が systm.h に追加されていて ftime(2) も実装するつもりだったと思われるのだがどこでも使われてないのである。 やっぱイラネってなったのだろうかねこれ。

18 int	lbolt;			/* time of day in 60th not in time */
19 int	time[2];		/* time in sec from 1970 */
20 int	fastime[2];		/* time in 1/60ths of sec */
 27 clock(dev, sp, r1, nps, r0, pc, ps)
 28 {
...
 38 	*lks = 0115;
...
 46 	if(++fastime[1] == 0)
 47 		++fastime[0];
...
106 	if(++lbolt >= HZ) {
...
109 		lbolt =- HZ;
110 		if(++time[1] == 0)
111 			++time[0];
...
145 	}
146 }

次回

なぜネットワーク対応しようとするとミリ秒単位での時刻管理が必要になるのか。 いよいよ David L. Mills 教授と NTP の話になるわけだけど前史からになると長いよなぁ。

ということでおそらくは PDP-11 上で動く現在でいうところのルーター OS の fuzzball 編である。 まぁそろそろブン投げて休載する可能性の方が高い。