The Unix and Internet Fundamentals HOWTO by Eric S. Raymond v1.4, 25 September 1999 堀田 倫英 と JF Project v1.4j, 8 December 1999 この文書では、PC クラスのコンピュータや Unix ライクなオペレーティン グ・システム、およびインターネットについての基礎的な事柄について、なる べく技術的な用語を使わないようにしながら説明します。 ______________________________________________________________________ 目次 1. はじめに 1.1 この文書の目的 2. 更新履歴 2.1 関連する文書 2.2 この文書の新しいバージョン 2.3 フィードバックと修正 3. あなたのコンピュータを解剖してみましょう 4. コンピュータの電源を入れた時には何が起こるのですか? 5. ログインすると何が起こるの? 6. シェルからプログラムを起動したら何が起こるの? 7. 入力装置と割り込みってどうやって動くの? 8. コンピュータはどうやって複数のプロセスをお互いに干渉し合わないようにしているの? 9. コンピュータはどのようにメモリに情報を記録しているの? 9.1 数値 9.2 文字 10. コンピュータはどのようにしてディスク上に情報を格納するの? 10.1 低レベルのディスクとファイルシステム構造 10.2 ファイル名とディレクトリ 10.3 マウントポイント 10.4 どうやってファイルを検索するの? 10.5 ファイルの所有権、パーミッション、セキュリティ 10.6 どうして動作がおかしくなるのでしょう? 11. コンピュータ言語っていうのはどうやって動くの? 11.1 コンパイル言語 11.2 インタプリタ言語 11.3 P コード言語 12. インターネットはどうやって動いているの? 12.1 名前とその位置 12.2 パケットとルータ 12.3 TCP と IP 12.4 HTTP: アプリケーションプロトコルの例 13. 日本語訳について ______________________________________________________________________ 1. はじめに 1.1. この文書の目的 この文書は、試行錯誤しながら学んでいる Linux とインターネットのユーザ のために書かれました。そのようにして専門技術を学んでいくことは実りの多 い方法なんですが、時には現在持っている基礎知識との独特なギャップを感じ ることがあるでしょう。そのギャップが、いま実際に何が行われているのかと いうことを頭の中に描けなくしてしまい、創造的に考えることや、効率的にト ラブルを解決することを難しくしたりしてしまうのです。 ここでは、すべてのことがどのように動いているのかを明解かつ平易な言葉で 述べてみようと思います。説明は PC クラスのハードウェアで Unix や Linux を使っている人達に合わせています。しかしそれでも、この文書では単に `Unix' と呼ぶことにします。私の説明は各プラットフォームや `Unix' のさ まざまな変種に関して共通のことだからです。 この文書では、あなたが Intel ベースの PC をお使いになっていると仮定し ます。もしあなたが Alpha や PowerPC、もしくはそれ以外の Unix マシンを お使いの場合でも違いは微々たるもので、基本的な設計思想は同じです。 いろいろなことを繰り返し述べたりはしませんので注意してください。つま り、ここに書いてある 1 語 1 語から意味を読み取って欲しいということで す。最初はこの文書をざっと斜め読みするのも悪くはありませんが、学んだこ とが少しまとまってきたら、また何度か繰り返して読んでみることをお勧めし ます。 この文書は随時発展していきます。私はユーザのフィードバックに応じて記述 を追加していくつもりですので、定期的に読むことをお勧めします。 2. 更新履歴 1.2 での新規項目: 「コンピュータはどのようにメモリに情報を記録している の?」の節 1.3 での新規項目: 「ログインすると何が起こるの?」と「ファイ ルの所有権、パーミッション、セキュリティ」の節 2.1. 関連する文書 ハックの方法を学ぶためにこの文書を読む場合は、 How To Become A Hacker FAQ も読んでおいた 方がよいでしょう (訳注: 日本語訳「ハッカーになるために」が JF にありま す)。 この文書には他の役立つ文書へのリンクがあります。 2.2. この文書の新しいバージョン `the Unix and Internet Fundamentals HOWTO' の新しいバージョンは、定期 的に comp.os.linux.help、 news:comp.os.linux.announce および news.answers に投稿されます。またこれらはもちろん LDP ホームページ等あ ちこちの Linux の WWW サイトや ftp サイトにアップロードされます。 この文書の最新バージョンは、 において、WWW 経由でご覧いただけま す。 2.3. フィードバックと修正 この文書に対して質問やコメントがある場合は、遠慮なく Eric S.Raymond(esr@thyrsus.com までメールしてください。いかなる提案や批判も 歓迎します。特に、個々の概念についての詳しい説明へのハイパーリンクがあ ればなお結構です。この文書に誤りを見つけた場合は、私にお知らせいただけ れば次のバージョンで修正します。どうぞご協力を。 3. あなたのコンピュータを解剖してみましょう コンピュータの中には、実際の処理を行うプロセッサ・チップが入っていま す。また、内部メモリも持っています。メモリのことを DOS/Windows な人た ちは `RAM' と呼びますが、Unix な人たちはよく `core(コア)' と呼びます。 プロセッサとメモリは、あなたのコンピュータの心臓部であるマザーボード 上にあります。 コンピュータには画面とキーボードがついています。またハードディスクやフ ロッピーディスクもあります。画面やディスクには、マザーボードに組み込む ためのコントローラ・カードがついており、これによってコンピュータがそれ らの出力デバイスの駆動を行うことができるようになっています。(キーボー ドは非常にシンプルな作りをしているので、専用のコントローラ・カードは不 要です。コントローラはキーボードの筐体自体に組み込まれています。) 以降では、これらのデバイスがどうやって動くのかといった細かい話に進んで いきます。ここでは、それらがどうやって協調して動くのかということに関す る、ぜひとも覚えていただきたい基礎的な事柄を解説していきます。 コンピュータの中にあるすべての内部パーツはバスで接続されています。物理 的には、バスは(ビデオ・カード、ディスク・コントローラやサウンド・カー ドなど、あなたがお持ちの)コントローラ・カードを差し込むものであり、あ なたのプロセッサ、画面、ディスクその他すべての間にあるデータの高速道路 です。 プロセッサは、自分以外のすべてのものを動作させるためにあるのですが、実 際には他のパーツを直接見ることができません。つまり、バスを通じて会話す ることしかできないのです。プロセッサが持っていて、非常に高速でしかも直 接アクセスできるサブシステムはメモリ(コア)だけです。このため、プログラ ムを動作させるためには、プログラムはコアの中にある必要があります。 コンピュータがプログラムやデータをディスクから読み出す際に実際に行って いることは、プロセッサがバスを使ってディスクのリード要求をディスク・コ ントローラに送ることです。しばらくしてからディスク・コントローラはバス を使ってコンピュータにシグナル(信号)を送ることで、データを読み込んでコ アの中の特定の位置に置いたことを知らせます。その後プロセッサはバスを 使ってそのメモリを見に行きます。 キーボードと画面もまたバスを経由してプロセッサと通信を行いますが、これ らが行うのはもっと単純な方法です。これについては後述します。今のとこ ろ、コンピュータの電源を入れたときに起こることを理解するにはこれで十分 です。 4. コンピュータの電源を入れた時には何が起こるのですか? プログラムが動いていないコンピュータは、電気で動く鉄のかたまりに過ぎま せん。電源が投入された時にコンピュータがまずやらなければならないこと は、オペレーティング・システムと呼ばれる特殊なプログラムを開始させるこ とです。オペレーティング・システムの仕事は、コンピュータのハードウェア を制御するというやっかいな処理を行なうことにより、他のコンピュータ・プ ログラムが動作する手助けをすることです。 オペレーティング・システムを起動する処理のことをブートといいます(これ は元々 ブートストラッピング (bootstrapping)といい、「自力で(`by your bootstraps')」立ち上がることの難しさを意味しています)。コンピュータは ブートの方法を知っています。なぜなら、ブートのための命令がコンピュータ のチップのうちの 1 つである、BIOS (またはBasic Input/Output System=基 本入出力システム)チップに埋め込まれているからです。 BIOS チップは、最小の番号がついているハードディスク(ブートディスク)上 の決まった位置にある、ブートローダと呼ばれる特殊なプログラムを探しま す(Linux ではブートローダは LILO と呼ばれています)。ブートローダはコア に読み込まれ、動作を開始します。本当のオペレーティング・システムを開始 させることがブートローダの仕事です。 ローダはこの処理を行うために、カーネルを探してコアに読み込み、そしてカ ーネルを始動させます。Linux をブートすると画面に "LILO" という文字が現 れ、続けてドットが 1 つずつ表示されていきますが、まさにこの時カーネル を読み込んでいるのです(表示される各ドットは、読み込んだカーネルコード のディスク・ブロックを表しています)。 (なぜ BIOS がカーネルを直接ロードしないのか、不思議に思われる方もい らっしゃるかもしれません。なぜブートローダで 2 つの過程を踏むのでしょ う? これはですね、BIOS っていうのはあまり賢くないからです。実際こいつ はとても頭が悪いので、ブートが終われば Linux は全く BIOS を使いませ ん。これは元々ディスクもあまり積んでいない原始的な 8 ビット PC のため に書かれたもので、カーネルを直接ロードするようなディスクアクセスは事実 上できないのです。ブートローダの過程においても、ディスク上の異なった場 所からいくつかのオペレーティング・システムを開始させることはできます が、これは Unix の機能では不可能なことなのです。) いったんカーネルが動作を開始すると、カーネルは周辺装置を検索してその他 のハードウェアを見つけ、そしてプログラムが動作するための準備を完了しま す。この処理は、通常のメモリ位置ではなく I/O ポート -- デバイスのコン トローラ・カードがコマンドを受け付けるために監視していると思われる、特 別なバスのアドレス-- にある値を書き込むことによって行われます。カーネ ルはランダムに値を書き込むわけではありません。カーネルには、どこのポー トに書き込みに行けばよいか、またそこにコントローラが存在した場合どのよ うに反応するべきかについて、あらかじめ組み込まれた情報を持っているので す。この処理のことをハードウェアの自動認識(autoprobing)といいます。 ブート時に表示されるメッセージのほとんどは、カーネルによるハードウェア の自動認識機構が I/O ポートを通してハードウェアにアクセスし、それが使 用可能であればそれらをマシンに組み込んでいる様子を示しています。Linux のカーネルはこの機能については良くできていて、他のほとんどの Unix に優 り、ましてや DOS や Windows よりは はるかに 優れています。実際、昔から の Linux 使いの方々は、Linux のブート時におけるハードウェアの自動認識 機能の賢さ(インストールの簡単さに通じる)が、Linux がフリーな Unix の一 連の実験段階から抜け出し、口うるさい大衆ユーザを魅了するようになってき た主な要因だったと考えています。 しかしながら、カーネルのロードが完了して動作を開始することで、ブート処 理が終わったわけではありません。まだほんの初期の段階です( ランレベル 1 と呼ばれることもあります)。この最初の段階の後、カーネルは制御を `init' と呼ばれる特殊なプロセスに引き渡します。`init' は管理のためのプロセス をいくつか立ち上げます。 init プロセスの最初の仕事は普通、ディスクが正常であるかどうかを確認す ることです。ディスクのファイルシステムとは壊れやすいものです。もしそれ らがハードウェアの故障や突然の電源遮断などによってダメージを受けていれ ば、Unix がすべての面で完全に立ち上がる前にファイルシステムの修復を行 う必要があります。後述の ``ファイ ルシステムはどうやって壊れるのか''の 節で、このことに触れます。 init の次の仕事は、いくつかのデーモンを起動することです。デーモンと は、やるべきことを待ちながらバックグラウンドでこっそり動く、プリント・ スプーラやメールサーバ、WWW サーバといったプログラムのことです。これら の特別なプログラムは、競合する複数の要求を調整しなければならないことが 往々にしてあります。これらをデーモンとする理由は、多数のプログラムのコ ピー(各々が 1 つの要求を処理して、それらがすべて同時に動いている)に、 お互いに他の処理に影響を及ぼさないかを確認させようとするよりは、ずっと 動きっぱなしで、すべての要求について知っている 1 本のプログラムを書く 方が簡単だからです。あなたのシステムで開始されるこの特殊なデーモン群は システムにより異なっているかもしれませんが、ほとんどの場合その中にはプ リント・スプーラ(プリンタの番人)が含まれます。 いったんすべてのデーモンが開始されると、ランレベル 2 に入ったことにな ります。次のステップではユーザに対する準備を行います。init は getty と 呼ばれるプログラムのコピーを起動することにより、あなたのコンソールを監 視します(場合によっては、それ以上の数の getty がダイヤルイン用のシリア ル・ポートを監視することもあります)。このプログラムは、あなたのコンソ ールに ログイン プロンプトを表示します。ここでランレベル 3 に到達し、 ユーザがログインしてプログラムを動かすための準備が完了したことになりま す。 5. ログインすると何が起こるの? (ログイン名とパスワードを入力して)ログインすると、あなたは getty およ びコンピュータに認識されます。getty は (そのまんまな名前ですが)login と呼ばれるプログラムを実行します。このプログラムはあなたがマシンの使用 を許可されているかどうかをチェックします。もし許可されていなければ、ロ グインしようとしても拒否されます。許可されていれば、login は管理のため の処理をいくつか行った後、コマンドインタプリタすなわちシェルを起動しま す。(ええ、gettyとlogin はひとつのプログラムにもできました。別々になっ ているのは、歴史的な経緯のため、今さら一つにするのは得策でないからで す。) システムがユーザにシェルを使わせる前にやっていることはもう少しありま す。これは後でファイルのパーミッションの説明をする時には理解している必 要があります。ユーザはユーザ名とパスワードで識別されます。このログイン 名は /etc/passwd というファイルから引きます。このファイルには、ユーザ アカウントについて書かれた行が並んでいます。 これらのフィールドのひとつは、アカウントのパスワードを暗号化したもので す。アカウントのパスワードとして入力した文字列は、パスワードフィールド と全く同じ方法で暗号化され、login プログラムはこれらが一致するかどうか をチェックします。この方法の安全性は以下の事実に基づいています。つま り、平文のパスワードを暗号化するのは簡単ですが、その逆の操作は非常に困 難です。したがって、誰かに暗号化したパスワードを見られたとしてもアカウ ントを使われることはありません。(これは、パスワードを忘れてしまうとそ れを調べる方法はなく、何か別のパスワードに変更するしかないということで もあります。) いったんうまくログインすると、あなたが使っている個人アカウントに対応す る権限が全て手に入ります。また、あなたはグループの一員としても識別され ます。グループは、システム管理者が設定したユーザの集合です。グループ は、個々のメンバーの権限とは独立の権限を持っています。ユーザは複数のグ ループのメンバーになれます。(Unix における権限がどう働くかについては、 後述の``パーミッション''に関する節をご覧ください。) (普通はユーザとグループは名前で参照しますが、これらは内部的には数値の ID で保持されていることに注意してください。パスワードファイルはアカウ ント名をユーザ ID に対応付けます。/etc/group ファイルはグループ名を数 値のグループ ID に対応付けます。アカウントやグループを扱うコマンドは、 この変換を自動的に行います。) あなたのアカウントのエントリにはホームディレクトリが含まれています。こ れは、個人のファイルが置かれる Unix ファイルシステム上の位置です。最後 に、アカウントのエントリにはシェルの設定も含まれます。これはあなたが入 力するコマンドを受け付けるために login が実行するコマンドインタプリタ です。 6. シェルからプログラムを起動したら何が起こるの? 通常のシェルでは、ログインすると(何か他のものにカスタマイズしていない 限り)'$' プロンプトを表示します。ここではシェルの文法や、画面に表示さ れるものについては触れません。そのかわり、ここで起こっていることの背景 について、コンピュータの視点から考察してみます。 ブートも終わり、あなたがプログラムを起動しようとする前に思い出していた だきたいことは、コンピュータの中には何かするべきことを待っているプロセ スの一群がいるということです。それらはすべてイベント を待っています。 イベントとは、あなたがキーを押したとかマウスを動かしたなどのできごとで す。また、あなたのマシンがネットワークに接続されていれば、そのネットワ ークを通してそのマシンに入ってきたデータパケットもイベントのうちの 1 つとなります。 カーネルもまたこれらのプロセスの仲間です。カーネルが特殊なのは、その他 のユーザプロセス が動作できるときにはそれらをコントロールし、また通常 マシンのハードウェアに直接アクセスできるただ 1 つのプロセスだからで す。実際、ユーザプロセスは要求を作成してカーネルに伝えることにより、キ ーボード入力を取得したり、画面に書き込んだり、ディスクの読み書きをした りしなければならず、ただ 1 つ自分でできることは、メモリの中でビット操 作や計算をしたりすることくらいです。これらの要求は、システムコールとし て知られています。 通常すべての I/O はカーネルを通して行われるので、カーネルは操作のスケ ジューリングを行ったり、各プロセスがお互いに影響を及ぼさないように守っ てやることができます。ごく一部の特殊なユーザプロセスでは、I/O ポートへ の直接のアクセス権を持つことにより、カーネルのあずかり知らないところで 動作することが許されるものもあります。これの一番典型的な例としては X サーバ(ほとんどの Unix マシンにおいて、他のプログラムの要求を処理して グラフィックス画面に描画するプログラム)があります。しかし我々はまだ X サーバまではたどりついていません。あなたはまだキャラクタ・コンソール上 のシェル・プロンプトを見ているだけなのです。 シェルも単なるユーザプロセスの 1 つであり、別に特殊なものではありませ ん。シェルは(カーネルを通して)キーボードの I/O ポートを監視することに よりキー入力を待ちます。キーが押されたことをカーネルが検出すると、それ らを画面にエコー(表示)すると同時にシェルに渡します。カーネルが `Enter' を検出すると、テキスト行をシェルに渡します。そしてシェルはそれらのキー 入力をコマンドとして解釈しようとします。 たとえば `ls' とタイプして Enter キーを押すと、Unix のディレクトリ一覧 表示機能が起動されます。シェルは内部に組み込まれている規則を適用して、 あなたが `/bin/ls' ファイルというファイルに入っている実行可能コマンド を走らせようとしていることを知ります。そしてシステムコールを発行して /bin/ls を新しい子プロセスとして起動することをカーネルに対して要求し、 カーネルを通して画面とキーボードにアクセスする権限をその子プロセスに与 えます。その後シェルはスリープし、ls が終わるのを待ちます。 /bin/ls は終了する際に exit システムコールを発行して実行終了をカーネル に通知します。するとカーネルはシェルを起こして(wake-up)動作を続行する よう指示します。シェルは次のプロンプトを発行して次の入力行を待ちます。 しかしながら、`ls' が実行されている間にあなたは別のことを行ってもかま いません(たとえばあなたが、とても長いディレクトリのリストを出している としましょう)。あなたは他の仮想コンソールに切り替えてそこでログイン し、Quake のゲームを始めてもよいのです。または、あなたがインターネット に接続しているとしましょう。あなたのマシンは /bin/ls を実行しながらメ ールの送受信を行うこともできます。 7. 入力装置と割り込みってどうやって動くの? キーボードは非常に単純な入力装置です。なぜ単純かというと、小さな単位の データを(コンピュータの標準機能により)非常にゆっくりと生成するだけだか らです。キーを押したり離したりすると、そのイベント信号がキーボード・ケ ーブルを伝わって、ハードウェア割り込みを起こします。 そのような割り込みを監視するのはオペレーティング・システムの仕事です。 各種の割り込みごとに 割り込みハンドラが用意されています。これはオペレ ーティング・システムの一部分で、 (キーを押した/離したなどを表す値のよ うな)関連するデータを処理できるようになるまで保存しておきます。 キーボードの割り込みハンドラが実際に行うことは、キーの値をコアの底の方 にあるシステム領域に格納することです。オペレーティング・システムが、現 在キーボードからの読み込みを行っていると見なされた何らかのプログラムに 制御を渡した時、そのプログラムは格納された値を調べることができるように なります。 ディスクやネットワーク・カードなど、より複雑な入力装置の場合も、これと よく似た方法で動作しています。ディスク・コントローラは、バスを使って ディスクへの要求が満たされたことを表わす信号を伝えていることは前にご紹 介しました。ここで実際には、ディスクは割り込みを発行しているのです。そ の後ディスクの割り込みハンドラは、取り込まれたデータをメモリ中にコピー し、要求を出したプログラムが後でそのデータを使えるようにします。 どの種類の割り込みにも、関連する 優先度レベルがあります。低優先度の割 り込み(キーボード・イベントなど)は、高優先度の割り込み(時刻の刻 み(clock ticks)やディスク・イベントなど)により待たされます。Unix で は、すばやく処理しなければならないようなイベントに対して、マシンの応答 性をスムーズに保つために高い優先度を割り当てるように設計されています。 OS ブート時のメッセージの中で IRQ 番号に関するものが表示されると思いま す。ハードウェアの設定ミスでよく起こるのが、2 つの異なったデバイスが、 事前の了解なしに同じ IRQ を使おうとしてしまうことです。 解決方法はこうです。IRQ は "Interrupt Request" (割り込み要求) の略で す。オペレーティング・システムはその開始時に、各ハードウェア・デバイス がどの番号の割り込みを使うのかを把握していなければなりません。2 つの異 なったデバイスが同一の IRQ を使おうとしてしまうと、割り込みが間違った ハンドラを駆動してしまうことがあります。通常これは少なくともそのデバイ スを探しに行き、OS の動作を狂わせたりクラッシュさせたりする要因となり ます。 コンピュータはどうやって複数のことを同時に行うことができるの? 実際にはこのようなことはできません。コンピュータは同時には 1 つのタス ク (つまり プロセス)だけしか実行できません。しかしコンピュータは、タス クを非常に高速に切り替えることができるので、鈍重な我々人間には、同時に 複数のことが行われているように見えてしまうのです。これは タイム・シェ アリング(訳注:時分割) と呼ばれています。 カーネルの仕事のうちの 1 つにタイム・シェアリングの管理というものがあ ります。これは スケジューラ と呼ばれる部分で行われており、その内部には 他の全ての(非カーネル)プロセスに関する情報が保持されています。カーネル 中では 60 分の 1 秒ごとにタイマ監視がオフになり、クロック割り込みを生 成します。この時どのプロセスが動作していようと、スケジューラはそのプロ セスを停止して動作を一時停止(suspend) させ、制御を別のプロセスに移しま す。 60 分の 1 秒という時間はそう長いとは感じないかもしれません。しかしマイ クロ・プロセッサは今日一日だけでも何万回というマシン命令を実行してお り、頻繁な割り込みは非常な負荷となります。このため多くのプロセスが動作 している場合、各プロセスは 1 回のタイムスライス(訳注: CPU 時間の割り当 て)ではほんのちょっとしか動作できません。 実際は、各プログラムは割り当てられたタイムスライスをほとんど使えないこ ともあります。I/O デバイスから割り込みが来た場合、カーネルは効率よく現 在のタスクを停止し、割り込みハンドラを駆動してからまた現在のタスクに戻 ります。高優先度の割り込みが殺到した場合、制御が奪われて通常の処理は動 作不能となります。この現象のことを スラッシング と呼びますが、幸運なこ とに最近の Unix ではほとんど起きることはありません。 実際、プログラムの動作速度は取得可能なマシンタイムによってのみ制限され ますが、このような制限はめったに発生することではありません (このルール の数少ない例外としては、サウンドや 3 次元グラフィックスの生成などがあ ります)。よく発生する事象としては、プログラムがディスク・ドライブや ネットワーク・コネクションからのデータを待たなければならない場合、実行 の遅延が起こります。 日常的に、同時に動作する多くのプロセスをサポートすることのできるオペレ ーティング・システムのことを、「マルチタスク OS」と呼びます。Unix 系の オペレーティング・システムは、当初よりマルチタスキングのために設計さ れ、Windows や Mac OS などに比べて非常にうまく機能しています。Mac OS の場合は、マルチタスキングは後から追加された機能で、あまりうまく実装さ れているとはいえません。効率的で信頼性のあるマルチタスキングは、Linux でネットワーキングや通信、および Web サービスを行うにあたり、優位性を 持つための重要な要素となっています。 8. コンピュータはどうやって複数のプロセスをお互いに干渉し合わないよう にしているの? カーネルのスケジューラは、複数のプロセスを時間で分割しています。また、 オペレーティング・システムは時間と同様に空間でもプロセスを分割します。 このため、各プロセスはお互いの作業メモリに入ることができません。たとえ 全てのプログラムを一緒に動かしたとしても、あるプログラムのバグが他のプ ログラムに害を与えることはありません。オペレーティング・システムが行う この問題に対する対処方法のことを、メモリ管理 と呼びます。 あなたの管理下にある各プロセスには、実行すべきコードを格納したり変数を 保持して結果を書き込むためのコアメモリの領域がそれぞれ必要になります。 この領域のセットは、読み込みのみの コードセグメント (プロセスの命令を 保持している)と、書き込み可能なデータセグメント (プロセス内のすべての 変数領域を保持している)から構成されます。データセグメントは各プロセス 間で全くユニークですが、2 つのプロセスが同一のコードを実行する場合、効 率的に管理するために、Unix は自動的に単一のコードセグメントを共有する ようにそれらを調整します。 効率が大切なのは、コアメモリが高価だからです。マシン上で動作しているす べてのプログラム全体を保持することができない場合も時にはあります。 X サーバのような巨大なプログラムを使っている時などは特にそうです。この問 題を解決するために、Unix では 仮想メモリ という手法を使います。この手 法では、プロセスのためのすべてのコードとデータをコアに持とうとはしませ ん。そのかわり比較的小さな ワーキングセット を保持します。プロセスの中 のそれ以外の情報は、ハードディスク上の特殊な スワップスペース 領域に残 されます。 プロセスが動作すると、 Unix はどれくらいのワーキングセットが変更される かを予測し、プロセス空間のうち必要最小限の部分をコアの中に持ちます。こ れを効果的に行うことは複雑でかつ手の込んだことなので、ここでそのすべて を説明することはしません -- しかしこの手法は、コードおよびデータの参照 においては、新しいものは古いものの近くのどこかを参照することが多く、ま たそれは連続して起こる傾向があるという事実に基づいています。 注意: 過去においては、2 つ前の段落の「時には」の部分は「ほとんど常に」 でした。コアのサイズは基本的にプログラムのサイズに比較して小さいため、 スワッピングが頻繁に起こりました。メモリは今日ではもはやそんなに高価な 物ではなく、ローエンドのマシンでも十分なメモリを積んでいます。コアに 64MB 以上ものメモリを積んでいる最近のシングルユーザ・マシンでは、X に 加えて一般的なジョブを組み合わせて走らせても全くスワッピングは起こりま せん。 この幸せな状況においてもなお、メモリ・マネージャ と呼ばれるオペレー ティング・システムの一部分は、未だに重要な役目を果たしています。メモ リ・マネージャは、あるプログラムが変更できるのは自分自身のデータセグメ ントのみであることを保証しなければなりません。つまり、プログラム中のコ ードが間違って、もしくは悪意を持ってデータを別の内容に書き換えることが 起こらないようにしなければなりません。メモリ・マネージャ はこれを行う ために、データおよびコード・セグメントのテーブルを保持しています。プロ セスがより多くのメモリを要求したり(通常はプログラムの終了時に)メモリを 解放したりするたびに、このテーブルは更新されます。 このテーブルは、MMUまたはメモリ管理ユニットと呼ばれる、下で動いている ハードウェアの特別な部分にコマンドを渡すために使われます。最近のプロ セッサ・チップは MMU を内蔵しています。MMU はメモリ領域を周りから保護 するという役目がありますので、領域を超えた参照は拒否され、特殊な割り込 みが発生するようになっています。 もし今までに "Segmentation fault", "core dumped" といった Unix メッセ ージを見たことがあれば、まさにこれが起こったということです。動作中のプ ログラムがそのセグメント外のメモリをアクセスしようとした場合、致命的な 割り込みが発生します。これはプログラム中にバグがあることを示していま す。この際にはプログラマが原因を追究できるように、コアダンプ という診 断情報が出力されます。 メモリのアクセスを分離する以外にも、プロセスをお互いから保護する状況が あります。また、ファイルのアクセスも制限して、バグっぽいプログラムや悪 意のあるプログラムがシステムの重要な部分を壊せないようにしたいと思うで しょう。これが、Unix に``ファイルのパーミッション''がある理由です。こ れについては後述します。 9. コンピュータはどのようにメモリに情報を記録しているの? コンピュータは何でもビット列(二進数の数値。小さなオン/オフのスイッチが たくさんあると考えてもかまいません)として持っていることは多分ご存知で しょう。この節では、コンピュータがガリガリ処理している文字や数値はこう いったビット列をどのように使って表すのかを説明します。 説明を始める前に、コンピュータのワードサイズについて理解しておく必要が あります。ワードサイズは、ある情報のかたまりをコンピュータが動かすとき に好む大きさのことです。技術的には、これはプロセッサのレジスタの幅で す。レジスタとは、コンピュータが数値演算や論理演算を行う際にデータを保 持するために使う領域です。一般にコンピュータをビットサイズを使って言い 表す(例えば「32 ビット」や「64 ビット」のコンピュータと呼ぶ)場合は、こ のことを言っているのです。 ほとんどのコンピュータ(386, 486, Pentium, Pentium II の PC)のワードサ イズは 32 ビットです。古い 286 マシンのワードサイズは 16 ビットです。 古い形式のメインフレーム機の多くのワードサイズは 36 ビットでした。いく つかのプロセッサ (例えば昔の DEC, 今の Compaq から出ている Alpha 等)の ワードサイズは 64 ビットです。64 ビットのワードサイズは、これからの 5 年間でもっと一般的になるでしょう。Intel は、Pentium II の後継として `Merced' と呼ばれる 64 ビットチップを計画しています。 コンピュータは、0 から始まりメモリサイズで決まる大きな値までが振られた ワードの並びとしてコアメモリを扱います。この値はコンピュータのワードサ イズによって決まります。そのため、286 のような古いマシンで大量のメモリ を使うには、苦労してひねくれたことをしなければなりません。ここではその 説明はしません。昔からのプログラマにはまだ悪夢でしょうしね。 9.1. 数値 数値はワードを使って表すか、ワードの組を使って表します。どうなるかはプ ロセッサのワードサイズで決まります。ワードサイズは 32 ビットがもっとも 一般的です。 整数値は数学的な意味での二進数によく似ていますが、実際には異なるもので す。低位のビットは順に 1, 2, 4, …となり、完全に二進数です。しかし、符 号付きの数を表すには2 の補数を使います。高位のビットは符号ビットであ り、値を負にします。負の数は常に、対応する正の数の全てのビットを反転さ せることによって得られます。これにより、 32 ビットのマシンは -2^31 か ら 2^31 - 1 までの数を表せます(^ は「べき乗」の演算です。つまり 2^3 = 8 となります)。32 番目のビットは符号を表すために使います。 一部のコンピュータ言語では、符号無し数値を使えます。これは 2 を基数と し、0 と正の数しか扱いません。 ほとんどのプロセッサと一部の言語は、浮動小数点の数値を扱えます(この機 能は、最近の全てのプロセッサチップに載っています)。浮動小数点を使う と、整数よりもずっと広い範囲を扱えますし、小数も表現できます。浮動小数 点を実現する方法はいろいろあり、ここで説明するにはちょっと難しすぎるの ですが、一般的な考え方はいわゆる「科学計算記法」に似ています。この記法 では(例えば) 1.234 * 10^23 といった書き方をします。この記法の数値は、 仮数 (1.234) と 10 のべき乗を掛けるための指数(23)に分けられます。 9.2. 文字 文字は普通、ASCII(American Standard Code for Information Interchange) と呼ばれるエンコーディングの 7 ビット単位のデータで表されます。最近の マシンでは、 128 個の ASCII 文字のそれぞれは、8 ビットのオクテットの下 位 7 ビットです。オクテットは、(例えば)6 文字の文字列が 2 つのメモリワ ードとなるようにメモリワードに詰め込まれます。ASCII のコード表を見るに は、 Unix のプロンプトで `man 7 ascii' と入力してください。 前の段落には紛らわしい点が 2 つあります。ちょっとした方は、「オクテッ ト」という言葉は公式には正しいのですが、実際に使われることがほとんどな い点です。大部分の人はオクテットをバイトの意味で使い、バイトとは 8 ビット長だと思っています。厳密に言うと、「バイト」という言葉の意味は もっと一般的です。例えば、昔はバイト長が 9 ビットの 36 ビットマシンが ありました (こんなものはもう二度と出ないでしょうが)。 重要な方は、世界中で ASCII を使っているわけではない点です。実際には、 世界の多くの場所では ASCII は使いものになりません。ASCII はアメリカ英 語で使う分にはいいのですが、他の言語のユーザが必要とするアクセント記号 や特殊記号が足りないのです。イギリス英語の場合でさえ、通貨のポンド記号 がないという問題があります。 この問題を解決しようという試みがいくつかあります。これらは全て、ASCII が使っていない高位ビットを追加して使い、ASCII 文字集合を 256 文字の集 合の下位の半分とします。これらのうち、もっとも広く使われているのはいわ ゆる `Latin-1' 文字集合です(もっと公式には ISO 8859-1 と呼ばれます)。 これは Linux, HTML, X のデフォルトの文字集合です。Microsoft は Latin-1 の亜流のバージョンを使っています。これは、正しい Latin-1 では歴史的な 理由から文字を割り当てずに残している領域に右・左の二重引用符といった文 字群を追加しています(これが原因で起こったトラブルに関するひどい話につ いては demoroniser の ページを見てください)。 Latin-1 はヨーロッパの主要な言語を扱えます。これには英語、フランス語、 ドイツ語、スペイン語、イタリア語、オランダ語、ノルウェー語、スウェーデ ン語、デンマーク語が含まれます。しかしこれでもまだ十分ではありません。 その結果、ギリシャ語、アラビア語、ヘブライ語、セルビア-クロアチア語と いった言語を扱うために全部で Latin-2 から Latin-9 までの文字集合ができ ています。詳しくは ISO alphabet soup のページをご覧ください。 究極の解決法は、Unicode と呼ばれる巨大な標準規格(および、これと同等の 双子である ISO/IEC 10646-1:1993)です。Unicode は、最も下位の 256 ス ロットについては Latin-1 と全く同じです。この上位の 16 ビットの空間に はギリシャ文字、キリル文字、アルメニア文字、ヘブライ文字、アラビア文 字、デバナーガリー文字、ベンガル文字、グルムキー文字、グジャラート文 字、オリヤー文字、タミル文字、テルグ文字、カンナダ文字、マラーヤラム文 字、タイ文字、ラオス文字、グルジア文字、チベット文字、日本語の仮名文 字、現代韓国のハングル文字の完全な集合、そして中国/日本/韓国(CJK)の漢 字を統合した文字集合があります。詳しくは Unicode のホームページ をご覧ください。 10. コンピュータはどのようにしてディスク上に情報を格納するの? Unix でハードディスクの内容を見ると、名前の付いたディレクトリやファイ ルのツリーがあります。通常それ以上のことを知る必要はありませんが、深い ところで何が行われているかを知っていると、ディスク・クラッシュが起こっ てファイルを救おうとする場合などに役立つこともあります。残念なことに、 ディスクの構造をファイルレベルから下の方に下りながら説明するのによい方 法がないのです。ということで、ここではハードウェアから上の方に向かって 説明しなければなりません。 10.1. 低レベルのディスクとファイルシステム構造 データを格納する場所であるディスクの表面領域は、環状のトラックが前もっ てセクタ(sector:扇形)に切り分けられ、ちょうどダーツ盤のように分割され ています。外側の端に近いトラックは、ディスクの中心部にあるスピンド ル(中心軸)に近いところより多くの領域を持つため、外側のトラックは内側の トラックより多くのセクタに区切られています。各セクタ(またはディスクブ ロック)は同一のサイズであり、最近の Unix では一般的に 1 キロバイ ト(1024 個の 8 ビット・ワード)です。各ディスクブロックはユニークなアド レスまたはディスクブロック番号を持っています。 Unix ではディスクをディスク・パーティションに分けます。各パーティショ ンはブロックが連続したもので、ファイルシステムとしてもスワップスペース としても、他のどのパーティションとも区別して使われます。一番小さい番号 がつけられたパーティションは、ブートするカーネルを置くためのブート・パ ーティション専用として取り扱われることがしばしばあります。 各パーティションは、スワップスペース(``仮想メモリ''を実装するために使 われます)かまたは、ファイルを保持するために使われるファイルシステムの いずれかです。スワップスペース・パーティションは単に連続したブロックの 並びとして扱われます。一方ファイルシステムは、ファイル名をディスクブ ロックの並びに対してマッピングするための方法を必要とします。なぜなら ば、ファイルというものは時間の経過につれそのサイズが大きくなったり小さ くなったり、また内容が変更されたりするので、各ファイルのデータブロック は連続した並びではなくなってゆき、そのパーティション中に散り散りばらば らになってしまうかもしれないからです(オペレーティング・システムは、必 要なときにそのパーティションのどこからか空いているブロックを見つけるこ とができるようになっています)。 10.2. ファイル名とディレクトリ 各ファイルシステムにおいては、名前からブロックへのマッピングは i-ノー ドと呼ばれる構造体を通して行われます。各ファイルシステムの「底」(最小 の番号がつけられたブロック)の近くに、これらのものをプールしておく場所 があります(この最小の番号がついた場所は管理やラベリングの目的で使われ ますが、ここではふれません)。各々の i-ノードは 1 つのファイルを表しま す。ファイルのデータブロックは、i-ノードがあるから成り立っているので す。 各々の i-ノードには、それが表すファイルのディスクブロック番号のリスト が入っています(実際は、これは正確ではありません。本当は小さなファイル にのみ当てはまりますが、これに関する具体的な話はここでは重要ではありま せん)。i ノードにはファイルの名前が入っているのではないことに注意して ください。 ファイルの名前はディレクトリ構造体の中にあります。ディレクトリ構造体 は、単に名前から i-ノードへのマッピングを行っているだけです。なぜなら Unix では、1 つのファイルは複数の本当の名前(またはハードリンク)を持つ ことができるからです。それらは単に、たまたま同じi-ノードを指している、 複数のディレクトリ・エントリなのです。 10.3. マウントポイント もっとも単純なケースでは、Unix ファイルシステム全体がただ 1 つのディス ク・パーティション内にあります。ある種の小さなパーソナル Unix システム でこのような構成になっていることがありますが、これは例外なのです。より 一般的には、ファイルシステムはいくつかのディスク・パーティションにまた がり、時には異なった物理ディスクにまたがっていることもあります。そこ で、たとえばあなたのシステムでは、小さな 1 つのパーティションにカーネ ルがあり、それより少し大きなパーティションに OS のユーティリティが入っ ていて、さらに大きなパーティションにユーザのホーム・ディレクトリがある としましょう。 システムのブート後すぐにアクセスする唯一のパーティションはルート・パー ティション です。(ほとんど常に)ここからブートが始まります。ここはファ イルシステムのルート・ディレクトリを保持していて、ぶらさがっているもの 全ての最上位ノードです。 システム内のそれ以外のパーティションは、複数パーティションにまたがるあ なたのファイルシステム全体がアクセス可能になるように、このルート・ディ レクトリに接続されなければなりません。ブート処理の途中あたりに、Unix システムはこれらルートでないパーティションをアクセス可能にします。これ は、そのパーティションをルート・パーティション上のあるディレクトリにマ ウントすることにより行います。 たとえば `/usr' と呼ばれる Unix のディレクトリがありますが、これはおそ らくパーティションへのマウントポイントであり、その中には Unix にインス トールされている多くのプログラムが入っています。しかしこれらは、ブート の最初の時点では必要とされないものです。 10.4. どうやってファイルを検索するの? さて、どうやってファイルをトップダウンで検索すればいいのでしょう。あな たがファイル(たとえば/home/esr/WWW/ldp/fundamentals.sgml) をオープンす る際に起こっているのは以下のようなことです: カーネルは、(ルート・パーティションにある) Unix ファイルシステムから検 索を開始します。そこに `home' と呼ばれるディレクトリがあります。普通 `home' は他のところにある大きなユーザ・パーティションへのマウントポイ ントで、まずそこへ行きます。そのユーザ・パーティションのトップレベルの ディレクトリ構造において `esr' というエントリがあるので、その i-ノード 番号を抽出します。その i-ノードへ行き、それがディレクトリ構成であるこ とを知り、次に `WWW' を探します。その i-ノード番号を抽出し、対応するサ ブディレクトリに行き、`ldp' を探します。そこもまたディレクトリの i-ノ ードです。それをオープンし、`fundamentals.sgml' の i-ノード番号を探し ます。その i-ノードはディレクトリではなく、そのかわりそのファイルに関 連するディスクブロックのリストを保持しています。 10.5. ファイルの所有権、パーミッション、セキュリティ 壊してはいけないデータをプログラムが事故で、あるいは悪意を持って壊さな いようにするため、Unix にはパーミッションという機能があります。これは 元々、同じマシン上にいる複数のユーザを保護することによってタイムシェア リングをサポートするために設計された機能であり、主に高価な共有ミニコン ピュータ上で Unix が使われていた時代に作られたものです。 ファイルパーミッションを理解するためには、 ``ログインすると何が起こる の''の章のユーザとグループの説明を思い出す必要があります。全てのファイ ルは所有ユーザと所有グループを持っています。これらの初期値はファイルの 作成者です。値を変えるには chown(1) と chgrp(1) というプログラムを使い ます。 ファイルに持たせられる基本的なパーミッションは「読み取り」(ファイルか らデータを読み取る許可)、「書き込み」(ファイルを修正する許可)、「実 行」(ファイルをプログラムとして実行する許可)です。それぞれのファイルは 3 組のパーミッションを持っています。つまり所有ユーザに対するパーミッ ション、所有グループに対するパーミッション、それ以外の全ての人に対する パーミッションです。ログインした時に得られる「権限(privileges)」は、パ ーミッションを表すビットが自分のユーザ ID か自分が所属しているグループ に一致するファイルの読み取り、書き込み、実行を行うことだけです。 パーミッションがが与える影響と、Unix がパーミッションをどのように表示 するのかを知るために、仮想的な Unix システムにおけるファイルのリスト表 示を見てみましょう。以下がそのリストです: snark:~$ ls -l notes -rw-r--r-- 1 esr users 2993 Jun 17 11:00 notes これは通常のデータファイルです。リスト表示を見ると、これはユーザ `esr' が所有しており、所有グループ `users' で作成されていることがわかりま す。我々が使っているマシンでは、全ての一般ユーザはデフォルトでこのグル ープに入っているでしょう。タイムシェアリングシステムのマシンで他によく 使われるグループとしては、`staff', `admin', `wheel' があります(自明な ことですが、個人使用のワークステーションや PC ではグループはあまり重要 ではありません。)。あなたが使っている Unix では、異なるデフォルトのグ ループ(多分ユーザ ID の後に示されているグループだと思います)を使ってい るかもしれません。 文字列 `-rw-r--r--' はそのファイルのパーミッションビットを表します。最 初のダッシュ(`-')は、ディレクトリビットの位置です。そのファイルがディ レクトリなら、これは `d' となります。それからその次の 3 つがユーザパー ミッション、その次の 3 つがグループパーミッション、その次の 3 つがそれ 以外に対するパーミッション(「世界中に対する」パーミッションと呼ばれる こともあります)です。このファイルの場合は、所有ユーザ `esr' はファイル の読み書きができ、`users' グループの他のユーザはファイルを読むことがで き、それ以外のユーザは全員ファイルを読むことができます。このパーミッ ションの組み合わせは、通常のデータファイルとしては典型的な設定です。 さて、まったく別のパーミッションを持つファイルを見てみましょう。この ファイルは GCC(GNU C コンパイラ)です。 snark:~$ ls -l /usr/bin/gcc -rwxr-xr-x 3 root bin 64796 Mar 21 16:41 /usr/bin/gcc このファイルは `root' と呼ばれるユーザと `bin' と呼ばれるグループに所 属しています。このファイルに書き込み(変更)ができるのは root だけです が、読み取りと実行は誰でも行えます。これは予めインストールされているシ ステムコマンドの所有者およびパーミッションとしては典型的な設定で す。`bin' グループは、システムコマンドをまとめるために一部の Unix シス テムで用意されています。 Unix の種類によっては、`bin' グループでなく `root' グループを使っている場合もあります(`root' グループと `root' ユ ーザは全く別物です!)。 `root' ユーザは、数値ユーザ ID が 0 のユーザの伝統的な名前です。これは は特別なユーザであり、他の権限を上書きできる特権を持っています。root によるアクセスは便利ですが、危険でもあります。root としてログインして いる時の入力ミスで、重要なシステムファイルを壊してしまうかもしれないか らです。同じコマンドを実行しても、一般ユーザのアカウントからであれば、 こういったファイルは壊せません。 root のアカウントは非常に強力なので、root へのアクセスは特に厳重に守る べきです。root のパスワードは、システムのセキュリティ情報の中でも最も 重要なものであり、あなたを狙っているクラッカーや侵入者が奪おうとしてい るものです。 (パスワードについて: パスワードは書き留めてはいけません。自分の名前や 友達、配偶者の名前など、簡単に推測できるようなパスワードを選んではいけ ません。これはクラッカーをとても助ける、この上なく悪い習慣です…。) 次に、3 番目のケースを見ましょう: snark:~$ ls -ld ~ drwxr-xr-x 89 esr users 9216 Jun 27 11:29 /home2/esr snark:~$ このファイルはディレクトリです(最初のパーミッションの項目が `d' である ことに注意)。このファイルに書き込みができるのは esr だけですが、他の誰 でも読み取りと実行は可能です。ディレクトリの場合、パーミッションの扱わ れ方は特別です。ディレクトリのパーミッションは、そのディレクトリに入っ ているファイルへのアクセスを制御します。 ディレクトリの読み取りパーミッションは簡単です。単に、そのディレクトリ の内容を調べられるかどうかを表します。書き込みパーミッションは、その ディレクトリ内でのファイルの作成・削除の許可を出します。実行パーミッ ションは、そのディレクトリの「検索」の許可を出します。ディレクトリの内 容を表示する時、ファイルを作成・削除する時は検索可能でなければなりませ ん。世界中から実行できるけれど世界中から読み取ることはできないディレク トリをよく見かけますが、これは不特定のユーザにディレクトリ内のファイル やディレクトリの取得を許すけれど、許すのは正確な名前がわかっている場合 に限るという意味になります。 最後に、login プログラム自身のパーミッションを見てみましょう。 snark:~$ ls -l /bin/login -rwsr-xr-x 1 root bin 20164 Apr 17 12:57 /bin/login これは、所有者の実行ビットの `s' を除けば、システムコマンドで想定され るパーミッションになっています。これは `set-user-id' または `setuid ビット' と呼ばれる特殊なパーミッションを示しています。 setuid ビットは普通、一般ユーザに root 権限を与える(ただし制限された形 で)必要があるプログラムに付けられます。setuid ビットが実行プログラムに 付けられていると、そのプログラムを実行している間は、プログラムファイル の所有者の権限を得ることができます。それがあなた自身の権限と一致してい るかどうかは関係ありません。 root アカウントそのものと同じく、setuid されたプログラムは便利であると 同時に危険でもあります。root が所有者の setuid されたプログラムを壊し たり改変できる人は誰でも、これを使って root 権限を持つシェルを起動でき ます。そのため、ほとんどの Unix システムでは、ファイルをオープンして書 き込みを行うと、 setuid ビットは自動的に落とされます。Unix のセキュリ ティに対する攻撃の多くは、セキュリティを破るために setuid されたプログ ラムをのバグを突こうとします。したがって、セキュリティに気を使うシステ ム管理者は setuid されたプログラムに特に注意を払っており、こういったプ ログラムを新しくインストールすることを嫌います。 先にパーミッションの説明を行ったときにはごまかしていた、細かいけれど重 要な話が 2 つあります。つまり、ファイルが最初に作成されたときの所有グ ループとパーミッションの付け方です。ユーザは複数個のグループに所属でき るのでグループの付け方が問題になるのですが、そのうちの一つ(/etc/passwd の項目で指定したもの)がユーザのデフォルトグループになり、ユーザが作っ たファイルは普通、このグループの所有となります。 最初に付くパーミッションビットの話はもう少し複雑な問題です。ファイルを 作るプログラムでは普通、最初のパーミッションを指定します。しかし、この 値は umask と呼ばれるユーザの環境変数によって修正されます。 umask 値 は、ファイルを作る際に落とすパーミッションビットを指定します。最もよく 使われる値であり、かつほとんどのシステムでのデフォルト値となっているの は -------w- すなわち 002 です。この値は世界中からの書き込みビットを落 とします。詳しくは、お使いのシェルの man ページで umask コマンドに関す る説明をご覧ください。 10.6. どうして動作がおかしくなるのでしょう? 前に、ファイルシステムは壊れやすいものであるとほのめかしたことがありま したね。ここで私たちは、ファイルを得るためには、気まぐれな長いチェイン をもつディレクトリや i-ノード参照の迷路の中で、石けり遊びみたいなこと をしなければならないことが分かっています。ここで、あなたのハードディス クに悪いところがあることが明るみに出たとします。 運が良ければ、いくつかのファイルデータが壊れるくらいですむでしょう。運 が悪ければ、ディレクトリ構造や i-ノード番号に矛盾が生じ、あなたのシス テムのサブツリーがまとめて奈落の底に落ちることでしょう。あるいはさらに 運が悪ければ、ファイル構造体が乱され、一つのディスクブロックや i-ノー ドへの参照がダブってしまったりするかもしれません。このような乱れは正常 なファイル操作にも広がり、もともとは正しい場所にあったデータまでも次々 と破壊されてしまいます。 幸いにもこのような偶発的な事故は、ディスクのハードウェアの信頼性が上 がってきたために、非常に少なくなってきました。それでもなお、間違った個 所がないことを保証するために、Unix が定期的にファイルシステムの整合性 を取りたいと考えることも無理のないことです。最近の Unix は、ブート 時(マウントする直前)に各パーティションに対して高速な整合性チェックを行 います。また、何回かブートするごとに、より詳細なチェックを行うので、こ の際は多少時間がかかります。 こんなことを聞くと、Unix とは恐ろしく複雑でトラブルが起こりやすいもの のように感じるかもしれませんが、これらブート時のチェックは、実際に被害 に遭う前に検出可能な問題を見つけたり修正したりすることにより、ユーザを 安心させるためなのです。他のオペレーティング・システムにはこういった機 能はなく、ブート処理の速度をちょっとばかり向上させる代償に、ユーザがび くびくしながら手作業で修復しようとせざるを得ないことを強いています(要 するに、最初からノートン・ユーティリティか何かをひとつ準備しておけ、と いうことですね)。 11. コンピュータ言語っていうのはどうやって動くの? すでに``プログラムはどうやって動くのか''については説明しました。どのプ ログラムも究極的には、そのコンピュータのマシン語命令のバイト・ストリー ムとして実行しなければなりません。しかし人間はマシン語をうまく処理する ことができません。たとえハッカーの間でさえも、これをやるのは滅多に見ら れない黒魔術になってしまいました。 カーネル自身の中でハードウェアへの直接のインターフェースをサポートする 少量のコードを除けば、現在ではほとんどすべての Unix のコードは高水準言 語で書かれています(ここで言う高水準とは、歴史的ななごりで、これら高水 準言語を「低水準」のアセンブラ言語と区別するためのものです。後者は基本 的にはマシンコードを包む薄い包み紙(ラッパー)です)。 いくつかの異なった種類の高水準言語があります。これらを説明するために、 ちょっと難しい話につきあっていただけたらと思います。プログラムのソース コードはある種の翻訳系を通して、マシンが実際に実行することのできるマシ ンコードに落とされます。 11.1. コンパイル言語 従来のほとんどの言語はコンパイル言語です。コンパイル言語はコンパイラと 呼ばれる(そのまんまな名前ですね)特別なプログラムによりバイナリのマシン コードとしての実行ファイルに変換されます。いったんバイナリが生成される と、再びそのソースコードを参照することなく、直接そのバイナリを実行でき ます(ほとんどのソフトウェアはコンパイルされたバイナリとして提供されて おり、そのコードをユーザが見ることはありません)。 コンパイル言語は高いパフォーマンスを示し、かつほとんど完璧な OS へのア クセスを提供しますが、それだけプログラムを書くのは難しくなります。 C 言語は、Unix 自体を書いている言語であり、これらコンパイル言語の中で も(その変種である C++ と共に)この上なく重要です。FORTRAN もコンパイル 言語で、技術者や科学者の中でいまだ根強く使われていますが、古く、かつよ り原始的です。 Unix の世界で主に使われているのはこれくらいです。Unix 以外の世界では、金融やビジネス用のソフトウェアとして COBOL が非常に広 範囲で使われています。 その他多くのコンパイル言語が使われてきましたが、それらのほとんどはその 生命を終えたか、または厳密には研究ツールになっています。もし新しい Unix 開発者になってコンパイル言語を使うのであれば、これはもう徹底的に C や C++ にするべきです。 11.2. インタプリタ言語 インタプリタ言語はインタプリタ・プログラムに依存しています。これはソー スコードを読んで、それをその場で逐次翻訳し、計算をしたりシステムコール を呼んだりするものです。そのコードを実行するたびに、そのソースは毎回解 釈され(かつ、そのインタープリタが存在し)なければなりません。 インタプリタ言語は一般的にコンパイル言語より低速で、またオペレーティン グ・システムやハードウェアへのアクセスを制限されることも多々あります。 一方、インタプリタ言語はプログラムを書くのが容易で、コンパイル言語に比 べてコーディングのエラーにも寛容です。 シェルや bc(1) や sed(1) や awk(1) といった多くの Unix ユーティリティ は、事実上はインタプリタ言語です。BASIC も普通インタプリタです。Tcl も そうです。歴史的に、最も重要なインタプリタ言語は LISP でした(成功した ものの中でもっとも発展してきました)。今日では Perl が非常に広範囲で使 われており、着実に評判を上げてきています。 11.3. P コード言語 1990 年以来、コンパイルと逐次翻訳の両方を使う混成言語がだんだんと重要 になってきています。P コード言語は、ソースが実際に実行するコンパクトな バイナリにコンパイルされるところはコンパイル言語に似ていますが、その形 式はマシンコードではありません。その疑似コード(またはP コード)は普通非 常に単純ですが、本来のマシンコードよりパワフルです。そのプログラムを動 かすと、P コードが解釈されます。 P コードはコンパイル言語に近い速度で動作できます(P コード・インタプリ タは非常にシンプルに作成でき、しかも小さくて高速です)。それにも関わら ず、P コード言語は良いインタプリタの柔軟性とパワーも持っています。 重要な P コード言語としては Python や Java があります。 12. インターネットはどうやって動いているの? インターネットがどうやって動いているのかを理解するのを助けるために、あ なたが典型的なインターネットの操作をしたときに何が起こっているのかにつ いて見ていきましょう。 the Linux Documentation Project の Web 上にある ホームページで、このドキュメントの先頭ページをクリックしたとします。こ のドキュメントは http://metalab.unc.edu/LDP/HOWTO/Fundamentals.html で、これはホスト metalab.unc.edu の WWW 公開用ディレクトリの LDP/HOWTO/Fundamentals.html にあることを示しています。 12.1. 名前とその位置 あなたのブラウザがまずしなければならないことは、そのドキュメントがある マシンへのネットワーク・コネクションを確立することです。これを行うため にはまず、ホスト metalab.unc.edu のネットワーク的な位置を見つけなけれ ばなりません(「ホスト」とは「ホストマシン」または「ネットワークホス ト」の略です。 metalab.unc.edu は典型的なホスト名です。これに対応する 位置は、実際にはIP アドレスと呼ばれる番号です(この用語の中の `IP' の部 分については後述します)。 これを行うため、ブラウザはネームサーバと呼ばれるプログラムに対して問い 合わせを行います。ネームサーバはあなたのマシン上にあるかもしれません が、あなたが対話しているサービス・マシン上で動いている可能性の方が高い でしょう。ISP に接続の申し込みをする時、セットアップ手続きの一部におい て、ほぼ間違いなく、インターネット・ソフトウェアに対してその ISP の ネットワーク上のネームサーバのIP アドレスを教えてやる必要があります。 異なったマシン上のネームサーバは、お互いに対話し合って、ホスト名を解決 する(ホスト名を IP アドレスにマッピングする)ために必要なすべての情報を 交換し、最新に保つようにしています。あなたのネームサーバ は、metalab.unc.edu を解決するために、ネットワークをまたがって 3 つか 4 つのサイトに問い合わせを行っているかもしれません。しかし、通常これ は(1 秒以内とかの)あっという間に行われます。 ネームサーバはあなたのブラウザに対して、metalab の IP アドレスは 152.2.22.81 であることを教えます。これを知ると、あなたのマシンは metalab と直接ビットデータの交換を行えるようになります。 12.2. パケットとルータ 次にここでブラウザがしようとするのは、metalab のウェブサーバに対して以 下のようなコマンドを送ることです。 GET /LDP/HOWTO/Fundamentals.html HTTP/1.0 実際に起こることは以下のようになります。コマンドはパケットとして送られ ます。これはビット列のブロックで、3 つの重要な情報といっしょに電報のよ うに包まれています。重要なものとは、始点アドレス(あなたのマシンの IP アドレス)、終点アドレス(152.2.22.81)、それからこれが WWW のリクエスト であることを示すサービス番号またはポート番号 (この場合は 80)です。 その後、あなたのマシンがそのパケットをケーブル(ISP へのモデム接続、ま たはローカルなネットワーク)へ送ると、そのパケットはルータと呼ばれる特 殊なマシンに届きます。ルータは自分のメモリ内にインターネットの地図を 持っています。その地図は必ずしも完全なものではありませんが、あなたの ネットワークに隣接する部分までを完全に網羅したものなので、ルータはその パケットを、インターネット上の他の場所にあるルータへどう届ければよいか を分かっています。 あなたのパケットは、宛先に届くまでの経路において、いくつかのルータを 通っていくかもしれません。それら相互のルータは、相手のルータがパケット を受信したことを認識するために、どれくらい送信に時間がかかっているかを 監視します。ルータはこの情報を使って、トラフィックを高速なリンクに向け ます。また、ルータはこの情報を使って、他のルータ(またはケーブル) が ネットワークからはずれてしまった時には通知を出し、可能であれば他の経路 を見つけて経路情報を補正します。 インターネットは核戦争を生き残るために設計されたという都市伝説がありま す。これは真実ではありませんが、インターネットの設計は、不確実な世界に おける変なハードウェアから確実な性能を得ることが非常に得意です。この直 接の理由は、インターネットの知的処理の部分は (電話ネットワークのよう な)少数の大規模なスイッチにではなく、何千ものルータに分散されていると いう事実のおかげです。つまり、不具合が起きてもそれは局所的なものにとど まり、ネットワークはこれを回避した経路を作ることができるのです。 いったんあなたのパケットが宛先のマシンに届くと、そのマシンはサービス番 号を使ってそのパケットをウェブサーバに入力します。ウェブサーバはコマン ドパケットの送り元 IP アドレスを見て、どこに応答を返せばよいかがわかり ます。ウェブサーバがこのドキュメントを返す時、それは数多くのパケットに 分割されます、それらのパケットのサイズは、ネットワークにおける伝送メ ディアやサービスのタイプにより異なります。 12.3. TCP と IP 複数パケットの転送がどうやって処理されるのかを理解するには、インター ネットが実際には階層構造になっている 2 つのプロトコルを使っていること を知らなければなりません。 下位のレベルであるIP (Internet Protocol)は、個々のパケットを始点アドレ スから終点アドレスに送る方法を知っています(このことから、これらの 2 つ のアドレスは IP アドレスと呼ばれているのです)。しかしながら、IP には信 頼性がありません。あるパケットが失われたり脱落してしまったりしても、始 点や終点のマシンにはこのことがわかりません。ネットワークの専門用語で は、IP はコネクションレスなプロトコルです。送信側はパケットを受信側に 対して送りつけるだけで、その ACK(肯定応答)を求めません。 しかしながら IP は高速かつコストの低い通信方法です。高速で手軽で信頼性 がなくても構わない場合もあります。例えば、ネットワーク対応の Doom や Quake (訳注:両方ともゲームの名前)をしているとき、各々の弾丸は IP パ ケットとして表現されますが、そのうちのいくつかがなくなっても OK ですよ ね? 上位レベルのTCP(Transmission Control Protocol =伝送制御プロトコル)は信 頼性を提供します。2 つのマシンが TCP で送受信を行う際、受信側は送信側 に対して ACK パケットを返します。送信側は一定時間内に ACK パケットを受 信できないと、そのパケットを再送します。しかも、送信側は各 TCP パケッ トにシーケンス番号を付けて、もし(分割されたパケットが)順番に届かなかっ た場合、受信側でパケットを再構成することができるようになっていま す。(これは、接続中にネットワーク・リンクが確立したりダウンしたりする 場合に起こります。) TCP/IP パケットにはまた、不良のリンクによるデータの破損を検出できるよ うにチェックサムが設けられています。このため、TCP/IP とネームサーバを 使っているユーザから見ると、ホスト名/サービス番号のペアの間には信頼で きるバイト・ストリームの経路があるように見えます。ネットワーク・プロト コル(のソフトウェア)を書く人は、これより下層レベルで行われるパケット化 やパケット組み立て、エラーチェック、チェックサムの生成やチェック、再送 といったことを全部考える必要はまずありません。 12.4. HTTP: アプリケーションプロトコルの例 ここで前の例に戻りましょう。ウェブブラウザとサーバは、TCP/IP の上位で 動作するアプリケーションプロトコルで話をします。これは単にバイト文字列 をやり取りするという単純な方法です。このプロトコルはHTTP (Hyper-Text Transfer Protocol)と呼ばれていて、そのコマンドのうちの 1 つが前に紹介 した GET コマンドです。 GET コマンドは metalab.unc.edu のウェブサーバにサービス番号 80 といっ しょに届き、そのサーバ上でポート 80 番を監視しているサーバ・デーモンに 渡されます。インターネット上のサービスのほとんどはサーバ・デーモンによ り実装されていて、これは単にポート上で入力を監視し、入ってくるコマンド を実行しているだけです。 インターネットの設計がある包括的なルールを持っているとすれば、それはす べてのパーツが可能な限りシンプルで、かつ人間が見てわかるようになってい るということです。HTTP およびその関連プロトコル( ホスト間で電子メール を送るのに使われる Simple Mail Transfer Protocol, SMTPなど)では、キャ リッジ・リターン (訳注:復帰コード)/ライン・フィード(訳注: 改行コー ド)で終わるシンプルな表示可能テキストを使ったコマンドがよく利用されま す。 この方法は少し非効率です。そのため、環境によってはガチガチにコード化さ れたバイナリ・プロトコルで速度を稼ぐことがあります。しかし経験上は、コ マンドを人間が記述・理解しやすいようにすることの利点は、トリッキーで分 かりにくくなるいう代価を払って得られる効率面でのわずかばかりの得を上 回っています。 したがって、サーバが TCP/IP 経由で送り返してくる応答もテキスト形式で す。応答の最初の部分は以下のようになります(ヘッダをいくつか消していま す): HTTP/1.1 200 OK Date: Sat, 10 Oct 1998 18:43:35 GMT Server: Apache/1.2.6 Red Hat Last-Modified: Thu, 27 Aug 1998 17:55:15 GMT Content-Length: 2982 Content-Type: text/html これらのヘッダに続いて 1 つの空行があり、その後(コネクションが切られる まで)Web ページのテキストが続きます。あなたのブラウザは単にそのページ を表示するだけです。ヘッダはブラウザに対して、その表示方法を指示しま す(代表的なものとして、Content-Type ヘッダは返されたデータが本物の HTML であることを伝えます)。 13. 日本語訳について 現在のバージョンは、堀田倫英さんの翻訳をベースに Linux Japanese FAQ Project が作成しました。翻訳に関するご意見は JF プロジェクト 宛に連絡してください。 改訂履歴を以下に示します。 v1.1j, 15 January 1999 翻訳: 堀田 倫英(sim@remus.dti.ne.jp) 校正: o 長谷川 靖(yaz-hase@qb3.so-net.ne.jp), o 早川 仁(uv9h-hykw@asahi-net.or.jp), o 中野 武雄(nakano@apm.seikei.ac.jp), o 遠藤 明(akendo@t3.rim.or.jp) v1.4j, 8 December 1999 更新: 藤原 輝嘉(fujiwara@linux.or.jp) 校正: 高城 正平(j96418@cc.nagano-nct.ac.jp)