UNIX 6th システムプログラミング - init その1
はじめに
半年くらい抱えていたとあることが、一応一段落したので、またブログを更新していこうと思っています。UNIX v7 code readingを継続するのと、それに加えてUNIX v6 システムプログラミング(code reading)というものを始めます。他にもいくつかネタはあるのですが、それは追々。
UNIX v6のカーネルは一通り理解できたつもりなので、次はカーネルが提供するサービスを使って、システムを構築・管理するユーザランドのシステムプログラムを見ていこうというものです。
カーネルは最低限のサービスを提供するものであり、その内容を真に理解するためには、サービスを使用する側の理解も必要だと考えています。これはカーネルだけでなく、ハードウェアとソフトウェア間や、ネットワークのレイヤ間などでも同様のことが言えるでしょう。
/etc/init
起動処理のおさらい
今回は/etc/init(以下init)を見ていきます。最初に、システムの起動時に実行されるカーネルのmain()の処理のうち、initに関連するものをおさらいします。
- main()はproc[0]をシステムプロセス(カーネルプロセス)として生成する
- proc[0]はswtch()とsched()の処理のみを行うスケジューリングプロセス
- proc[0]はnewproc()を実行してproc[1]を生成する
- proc[1]はinitを実行する
- 実行直前に自身のモードをユーザモードにする
これでカーネルとしての起動処理は完了し、ここからカーネルの処理の中心は、クロック割り込みなどを除き、基本的にはユーザプロセスからの要求を対処することになります。
initの仕様
init仕様を見る前に、上に挙げたことからいくつかのことが推測できます。
- initはシステムのユーザがシステムを使える環境を構築しているはず
- proc[0]はスケジューリングプロセスなので、initが環境を構築しないと、誰もシステムを使える状況にならない
- ユーザはシェルからシステムを操作する。そして端末を使ってシステムをやり取りする
- これらから推測すると、initは端末を開き、ログインプログラムとシェルプログラムを実行するはず
- 複数端末が使える環境にある場合は、それぞれの端末のオープン処理とログイン・シェルプログラム実行を行うはず
というわけで、マニュアルを見てinitの仕様を実際に確認します。
http://man.cat-v.org/unix-6th/8/init
カーネルの中身を追っていたときは、マニュアルの2, 4, 5章あたりを中心に見ていましたが、システムプログラムを追うときには1, 5, 8章あたりが重要になってきそうです。
Descriptionの部分を引用します。
DESCRIPTION Init is invoked inside UNIX as the last step in the boot procedure. Generally its role is to create a process for each typewriter on which a user may log in. First, init checks to see if the console switches contain 173030. (This number is likely to vary between systems.) If so, the console typewriter /dev/tty8 is opened for read- ing and writing and the Shell is invoked immediately. This feature is used to bring up a single-user system. When the system is brought up in this way, the getty and login rou- tines mentioned below and described elsewhere are not used. If the Shell terminates, init starts over looking for the console switch setting. Otherwise, init invokes a Shell, with input taken from the file /etc/rc. This command file performs housekeeping like removing temporary files, mounting file systems, and start- ing daemons. Then init reads the file /etc/ttys and forks several times to create a process for each typewriter specified in the file. Each of these processes opens the appropriate type- writer for reading and writing. These channels thus receive file descriptors 0 and 1, the standard input and output. Opening the typewriter will usually involve a delay, since the open is not completed until someone is dialed up and carrier established on the channel. Then /etc/getty is called with argument as specified by the last character of the ttys file line. Getty reads the user's name and invokes login (q.v.) to log in the user and execute the Shell. Ultimately the Shell will terminate because of an end-of- file either typed explicitly or generated as a result of hanging up. The main path of init, which has been waiting for such an event, wakes up and removes the appropriate entry from the file utmp, which records current users, and makes an entry in /usr/adm/wtmp, which maintains a history of logins and logouts. Then the appropriate typewriter is reopened and getty is reinvoked. Init catches the hangup signal (signal #1) and interprets it to mean that the switches should be examined as in a reboot: if they indicate a multi-user system, the /etc/ttys file is read again. The Shell process on each line which used to be active in ttys but is no longer there is terminated; a new process is created for each added line; lines unchanged in the file are undisturbed. Thus it is possible to drop or add phone lines without rebooting the system by changing the ttys file and sending a hangup signal to the init process: use ``kill -1 1.''
美味しいところを摘むと、こんな感じです。
- コンソールスイッチを見て、single user mode(0173030)かどうかチェック
- single user modeならば
- システムコンソール用に/dev/tty8端末をstdin, stdout用に開き、シェルプログラムを実行する
- ログイン処理は行われない
- single user modeでなければ
- (プロセスを生成し、)シェルプログラムを駆動し、/etc/rcに書かれている処理(一時ファイルの削除など)を行わせる。これはお掃除専用のプロセス
- /etc/ttysを読み、それぞれの端末にプロセスを生成する。システムコンソール用の端末/dev/tty8の情報も/etc/ttys中に含まれている様子
- それぞれのプロセスは適切な端末をstdin, stdout用に開く
- 端末のオープン処理は、ユーザのダイアルアップ接続&接続確立処理を待たないといけないため、遅延が発生する
- それぞれのプロセスは、/etc/ttysファイルの各行の最後の文字列を引数に、/etc/gettyを実行する。/etc/gettyは、ユーザのログイン処理、シェルプログラムの実行を行う
- utmpに現在実行中のユーザ情報を記録する
- /usr/adm/wtmpに、ユーザのヒストリ情報(誰が過去にログインしていたか)を記録する
- initは、端末からSIGHUP(hangup, signal #1)が送られてくると、utmpから該当エントリを削除し、/usr/adm/wtmpに該当エントリを追加する
- single user modeならば
いくつかのキーワードがでてきました。それらについてもマニュアルに記述がありました。/etc/rcと/etc/ttysはコードも載せておきます。
これらについてはinitを実際に読むときに、必要に応じて参照していくつもりです。
- /etc/rc
- ttys
- utmp
- wtmp
あと、"SETTING UP UNIX - Sixth Edition"を見ると、/dev/tty8というのはシステムコンソール端末用のスペシャルファイルのようですね。initは起動時の処理なので、この資料にも結構お世話になりそうな予感です。
http://minnie.tuhs.org/PUPS/Setup/v6_setup.html
Special Files ... snip ... The same goes for the character devices. Here the names are arbitrary except that devices meant to be used for teletype access should be named /dev/ttyX, where X is any character. The files tty8 (console), mem, kmem, null are already correctly configured. ... snip ... Multiple Users If UNIX is to support simultaneous access from more than just the console teletype, the file /etc/ttys (ttys-V) has to be edited. For some historical reason tty8 is the name of the console typewriter. To add new typewriters be sure the device is configured and the special file exists, then set the first character of the appropriate line of /etc/ttys to 1 (or add a new line). Note that init.c will have to be recompiled if there are to be more than 20 type- writers. Also note that if the special file is inaccessible when init tries to create a process for it, the system will thrash trying and retrying to open it.
コンソールスイッチというものが、明確にはよくわかっていないのですが、PDP-11/40にマッピングされているレジスタの一つで、システムをsingle user modeかmultiple users modeか、どちらで動かすか決めるものだと思います。ハードウェア的に、スイッチをパチパチいじって値を操作するのでしょうか?
終わりに
長くなってきたので、ここで一端切ります。initがどのような処理を行うのか、だいたい想像がついてきました。次回は実際にinitのコードを追っていきます。
ところで、initには駆動処理以外に、(親を失い)宙ぶらりんになったプロセスを処理する責務もあるのですが、それについてマニュアルで触れられていないような……見落としていますかね?