/sbin の謎 〜/usr マウント前のシンプルコマンド群〜

SunOS 4.1.4
YAMAMORI Takenori


SunOS 4.1.4 の /sbin には、たった7つのコマンドしかありません。しかも、これらはブート時、/usr をマウントする前までに一時的に使用されるだけです。/sbin の内容は、少なければ少ないほどシステムとして美しく、シンプルを極める /sbin のコマンド群には一種の美学を感じます。
/sbin にある7つのコマンド

SunOS 4.1.4 の /sbin には、次のように 7つのコマンドのみが存在します。


$ ls /sbin
hostconfig  ifconfig	intr	    sh  
hostname    init	mount

OSのブート時、これらのコマンドが実行される段階では、まだ /usr はマウントされていません。したがって、libc.so.* もまだ使用できず、/sbin のコマンドはすべて静的リンクされたバイナリです。


/sbin の必要性

そもそもの UNIX のディレクトリ構成は、/bin/usr/bin に基本コマンドがあり、/etc/usr/etc にシステム管理用コマンドがあるという感じでした。この時点では /sbin はまだありませんでした。

その後、SunOS 4.1.4 や Solaris 2.x/7/8/9 系では、/bin/usr/bin が統合されて /bin は単なる /usr/bin へのシンボリックリンクとなりました。同様に /lib も統合されて、ディレクトリごと /usr/lib へのシンボリックリンクです。また、 /etc についても、ここにはバイナリの実行ファイルは直接置かないことになり、例えば /etc/mount などのバイナリが個別に /usr/etc 以下へのシンボリックリンクになりました。 (Solaris 2.x/7/8/9 系では /usr/etc/usr/sbin と改名されました)

/usr 以下の各ディレクトリは、同じOSのマシン間で全く共通のものであり、 ディスクレスやデータレスのクライアントは /usr をNFS で(リードオンリーで)マウントして共有します。 また、スタンドアロン(ローカルディスク)のマシンであっても、/usr は通常、/ とは別パーティションにします。

なるべく多くのファイルを共有し、ハードディスクの使用効率をよくするためには、なるべく多くのディレクトリやファイルを、共有可能な /usr ディレクトリ以下に配置することが望まれます。/bin/usr/bin の統合、/lib/usr/lib の統合、/etc の一部のファイルの個別の /usr/etc へのシンボリックリンクが行なわれたのはこのためと考えられます。

さて、ここでシステムのブート時の問題を考える必要があります。

ブート時に、カーネルは、/ を自らマウントします。問題はそのあとです。init を実行し、/usr をマウントするまでの処理を、/usr 以下の存在しない、/ だけ(/etc はある)の環境で行なわなければならないのです。

以前のシステムでは、/etcinitmount があり、また、/bin/usr/bin とは別に存在していたため、これら /etc/bin とに存在するコマンドだけを使って rc スクリプトを実行し、/usr をマウントすることができました。

ところが、SunOS 4.1.4 などでは /usr のマウント前には、 /bin すら使えず、また、/etc には実行バイナリの実体はありません。

そこで、/usr マウント前にどうしても必要な、 本当に必要最小限のコマンドのみを置くためのディレクトリとして、/sbin が作られたのです。


/sbin は少なければ少ないほどよい

/sbin に置かれるコマンドは、本当に必要最小限のものです。 /sbin に置く必要の無いコマンドは、すべて /usr 以下の /usr/bin/usr/etc/usr/sbin)に置いて NFS で共有するという発想です。

また、/sbin に置かれているコマンドも、それ自体のバイナリは共有可能であり、ただ、/usr のマウント前に必要なために /sbin に置いているに過ぎません。このため、ディスクレスクライアント用の /sbin については、その NFS サーバ上では、複数のディスクレスクライアント用の /sbin 以下の各コマンドをハードリンクして、バイナリ実体を共有するのが普通です。

また、前述の通り /lib/usr/lib に統合されたため、/sbin にあるコマンドは、libc.so.* を含む、一切の共有ライブラリを使用できず、static link されたバイナリでなければなりません。static link のバイナリは、サイズが大きくなるため、あまり多くのコマンドを置くべきではありません。この意味でも、/sbin に置くバイナリは、必要最小限に厳選しなければなりません。


/sbin に置くコマンドの吟味

SunOS 4.1.4 の /sbin に置かれている7つのコマンドを、それらが本当に必要かどうかここで吟味してみましょう。

・init
システムの起動のために init が必要なのは、もちろん自明です。
・mount
/usr をマウントするため、当然 mount が必要です。ただし、mount は必要でも umount は必要ないため、/sbin には置かれていません。また、mount が扱えるファイルシステムタイプも、UFS と NFS に限定されていて、その他の hsfs, tmpfs, pcfs などは /usr/etc/mount_hsfs などのように mount 内部から別コマンドを呼び出す形で分離されています。
・sh
rc スクリプトは sh スクリプトであるため、これを実行するためにも sh は必要です。
・ifconfig
/usr を NFS でマウントするためにネットワークを立ち上げる必要があります。
・hostname
ホスト名を設定・参照するために必要です。
・hostconfig
ホスト名を bootparams 経由で設定するために必要です。
・intr
mount コマンドなどで、SIGINT を受け付けるようにするため必要です。

以上、7つのコマンドのどれもが必然性を持って置かれていることがわかります。


/sbin に置くコマンドを減らす工夫

・cat は置かない

SunOS 4.1.4 では、例えば /etc/hostname.le0 というファイルの中に自分のホスト名を書いておき、/etc/rc.boot の中で、概念的には、

  hostname `cat /etc/hostname.le0`
を実行することによって、ホスト名を設定します。

このため、単純な発想では 「cat コマンドも必要」ということになってします。しかし、cat/sbin に置いてしまっては、美しさが失われます。そこで、/etc/rc.boot では、shcat() というシェル関数を定義して、これに cat の代わりをさせているのです。

・fsck も置かない

/usr をマウントするには、その前に fsck しなければなりません。しかし、その fsck コマンドは /usr の下にあります。どうすればいいのでしょうか。その答えは、/usr をまず Read Only でマウントし、その状態で /usrfsck し、(実際には、多くの場合 clean チェックのみとなりすぐ終る)そこで再度 /usr を Read/Write でマウントするという方法を用いています。

トリッキーな感じがしますが、考えてみれば / ファイルシステム自体、カーネルがまず Read Only でマウントし、それを fsck のあと Read/Write で remount しているのと同じことであるため、なるほどという感じです。


このほか、さらに考察すると、コマンドの実行内容が単純と思われる intr は、mount のオプションとして吸収統合してしまうとか、さらに、同じく実行内容が単純な hostnameintr を argv[0] 切替え方式の単一バイナリでまとめてしまうとか、そこまでするなら7つのコマンドすべてを単一バイナリでまとめてしまうなどの改良案が考えられます。

●ブート後には /sbin 以下は実行されない

/sbin/usr をマウントする前だけに必要なものであり、/sbin にある7つのコマンドはすべて、同じ名前のコマンドが /usr 以下にもあります。(というか、/usr 以下にあるものが本来のコマンドで、インストール時にその一部が /sbin にコピーされているのです)

7つのコマンドのうち、以下の5つは同じコマンドが /usr/etc にあります。

hostconfig  ifconfig    init        intr        mount

なお、これらは、/usr/kvm/boot にもあります。(カーネルアーキテクチャに依存しないこれらのコマンドが、kvm にあるというのも、また別の謎です)

sh はもちろん /usr/bin(=/bin) に同じものがあります。
(ここで、JLE版の場合は /usr/bin/sh が JLE版の sh で置き換えられていることがわかります)
また、/usr/kvm/stand にも sh があります。

最後に、hostname は、/usr/bin(=/bin) に同じものがあります。

というわけで、/sbin に置かれているコマンドと同名のコマンドをブート後に実行する場合、/sbin のコマンドは使われません。この事実からも、/sbin はあくまで /usr をマウントする前に使うためだけのものという、徹底したポリシーを感じます。

なお、正確には、ブート時に /etc/rc/etc/rc.local に処理が移った段階で、すでに /sbin は使われなくなります。/etc/rc.boot/etc/rc.single では、PATH/sbin が含まれていますが、/etc/rc/etc/rc.local では、/sbinPATH から外されているのがわかると思います。

このため、たとえば ifconfig コマンドに注目してみると、/etc/rc.boot の中で実行される ifconfig は、/sbin/ifconfig であるのに対し、/etc/rc.local の中で実行される ifconfig は、/usr/etc/ifconfig になります。


/sbin のその後

このような厳格なポリシーを持つ /sbin ですが、その後はどうなったでしょうか。

Solaris 2.x/7/8/9 系では、/sbin に置かれるファイルは SunOS 4.1.4 よりもかなり増えました。/sbin には、static link のバイナリしか置かないはずでしたが、Solaris 2.x/7/8/9 系では、なんと dynamic link のバイナリまで置かれています。/usr がないのにどことリンクしているかというと、/etc/lib 以下に、一部の共有ライブラリを持ってしまっています。libc.so.* こそないものの、これはかなり苦肉の索の感があり、美しさが失われていると言えます。

では、Free系 UNIXの、Linux や NetBSD/OpenBSD/FreeBSD ではどうでしょうか。こちらは /sbin 以前に、依然として /bin/usr/bin は別で、/lib/usr/lib も別です。そして、/sbin には、fsck などを含むいろいろな管理コマンドが多数、べたべたと置かれています。SunOS とは違って、/sbin に存在するコマンドは /sbin 以外のディレクトリにはありません。このため、ブート後も /sbin が必要です。

このあたりで、すでに考え方が違っていて、おそらく、「/sbin はシステム管理のための重要な基本コマンド」 というふうに考えられているのだと思います。

しかし、本来の SunOS での /sbin の考え方に立ち戻り、システムとしての美しさも考えるべきだと思います。


To 『SunOS 4.1.4 の謎』index

To「謎の処理系 SunOS 4.1.4 with Linux/FreeBSD/Solaris」Home
yamamori