オペレーティングシステム 設計と実装 覚書
about
タネンバウムのオペレーティングシステムの覚書
1. オペレーティングシステム概論
物理的装置
ブラウン管、集積回路などのハードウェア。電子工学の扱う範囲。
マイクロアーキテクチャ層
物理的装置の機能ユニット。CPU内のレジスタ*1や演算ロジック装置*2を含むデータパスなど
データパスの操作はマイクロプログラムと呼ばれるソフトウェアによって制御される
マシン語
50~300程度の命令で構成される。マシン内のデータ移動、演算、比較を行う。
オペレーティングシステム
最も基本的なシステムプログラム(コンピュータ自身の操作を管理する)。全コンピュータ資源を制御し、アプリケーションプログラム(ユーザの希望する仕事をする)の実行環境を提供する
ハードウェアの煩雑さをOSがラップする
1.1.1 拡張マシンとしてのオペレーティングシステム
OSは拡張マシンもしくは仮想マシンをユーザーに提供する機能といえる。
Ex. NECのPD765と互換のコントローラーチップを使ったフロッピーの入出力
最も基本的なコマンドはread
とwrite
で、各々13のパラメータを必要とし、9バイトで構成される。各パラメータのセッティングやモータのオンオフを把握する必要がある
このハードウェアに関する実態をプログラマから隠し、単純な名前付きのファイルの読み書きに落とし込むのがOSの役割だ。
1.1.2 資源管理としてのオペレーティングシステム
OSの基本的な役割はユーザに使い勝手のいいインタフェースの提供である。パラフレーズすれば、プロセッサ・メモリ・I/Oデバイスを競って要求するプログラムに対し、秩序を保ち、あるいは制御する役割を提供するといえる。
OSはユーザごとの資源の管理・保護・共有と同様に、時間と空間の2つの軸に多重化されたから観点も対象にする。
1.2 オペレーティングシステムの歴史
コンピュータアーキテクチャとともにOSについて概説する。
最初の計算機はイギリスのチャールズ・バベッジにより考案されたが、資金問題により完成に至らなかった。
1.2.1 第1世代(1945-55) 真空管とプラグボードの時代
初期の計算機は40年代から50年代にかけて登場する。リレーや真空管が用いられており、サイクルタイムは秒のオーダーであった。
当時のすべてのプログラミングはマシン語であり、場合によってはプラグボードの配線を行っていた。50年代中ごろからはプラグボードに代わり、パンチカードが使用されるようになる。
1.2.2 第2世代(1955-65) トランジスタとバッチ処理
トランジスタの発明によりコンピュータの信頼性が向上し、工業生産されるようになった。今日のメインフレームである。
プログラムを実行するためには、紙上にプログラムを書き下し、そのあとにカードにパンチを行う。プログラムの記述されたカードを入力装置に読ませ、コンピュータに演算させていた。
これらの処理を1台の計算機で行うのは効率がわるいため(1つのジョブを完遂するために1台のコンピュータを使用すると都度オペレーションが求められる)、バッチシステムが考案された。
バッチシステムでは一定量のジョブ(プログラムの集積)がたまると一括で処理を行い、出力テープに結果を出力する。出力されたテープはプリンタによってプリントされる。いかに典型的なFMSジョブの構造を示す。
- $JOB
実行時間や課金用アカウント番号、プログラマの氏名が記載
- $FORTRAN
OSにFORTRANコンパイラをシステムテープから読み込み、コンパイルさせる - $LOAD
OSにコンパイルしたオブジェクトプログラム*3を主記憶装置に読み込みを指令 - $RUN
プログラムの実行をOSに指示 - $END
プログラムの終端を指示
1.2.3 第3世代(1965-80) ICとマルチプログラミング
全時代の1401*4や7094*5には命令セットに互換性がなく、IBMと顧客の両者にとって問題になっていた。
System/360
IBMは上記の問題を解決するためにSystem/360シリーズを開発した。これらのマシンはすべての同じアーキテクチャで同一の命令セットを保有したので、特定のシステム用に書かれたプログラムが同一ファミリマシンでも論理的に動作可能になった。
OS/360
それは結果として、OS/360を含むすべてのソフトウェアがすべてのマシン上で動作することでもある。様々な使用環境での動作させるために常にバグフィックスが行われる巨大なOSとなった。
第3世代のOSで導入された技術
マルチプログラミング
7094では実行中のジョブの要求するテープや他の入出力があると、CPUはI/Oの操作完了までアイドル状態になった。
そこで解決法として、メモリをいくつかのパーティションに分割し、別々のジョブを入れることが考案された。あるジョブがI/Oの操作完了を待つ間に、他のジョブを実行することが可能だ。
スプーリング
出力データを一時的にバッファに保存すること。これによりカードをセットするとすぐに出力が行われ、オペレータの頻繁なテープ運びは不要になった。
タイムシェアリング
1台のコンピュータをユーザ単位で共有し、サービスの利用を必要とするユーザにだけCPUの順番を割り付する。コンピュータが遊んでいるときは巨大なバッチを処理することも可能であった。TSSの本格的な初めての実装はCTSS*6である。
MULTICS
CTSSの後継として開発されたOS。汎用性をめ目指し、コンピュータユーティリティを目標していた。階層型ファイルシステムを初めて採用し、シンボリックリンクも実装されたいた。
UNIX
MULTICSプロジェクトに従事していたベル研究所のケントンプソンによりミニコン用のOSとして、実装されたものにルーツを持つ。System VやBSDといった派生種が登場したが、現在ではPOSIXと呼ばれる標準規格がIEEEにより標準化されている。
第4世代(1980-現在) パーソナルコンピュータ
大体は知ってる話なので割愛
ネットワークオペレーティングシステム
ユーザは複数のコンピュターの存在を意識しており、遠隔のPCにアクセス可能。マシンは相互に独立(ローカルなOSで実行される)しているが、あるマシンから他のマシンにファイル操作することができる。
分散オペレーティングシステム
現実の構成が複数コンピュータであっても、ユーザには単一プロセッサであるように振る舞う。アプリケーションが同時に異なるプロッセサで実行されてもユーザは意識することなく、OSが並列最適化を担う。
1.2.5 MNIX3の歴史
ノーライセンスのUNIXもどきをタネンバウムたちが頑張って書いたよって話。まぁ、これからそれを読むから仔細はいいよね。
1.3 オペレーティングシステムの概念
オペレーティングシステムとユーザプログラムの接点は「拡張命令」によって定義されている。こレラはいわゆるシステムコールだ。
1.3.1 プロセス
プロセスは基本的には実行時のプログラムだ。各プロセスに関連しているのがアドレス空間であり、プロセスから読み書きできるメモリ領域のリストである。
あるプロセスが一時的に中断されても、その時点と厳密的に同じ状態で差異化する必要がある。プロセスが中断される際には、どこかに明確に格納する必要があるということだ。プロセステーブルは各プロセスの固有のアドレス空間以外の場所にプロセスの情報を格納する場だ。これらは構造体(または連結リスト)の配列としてプロセスごとに作られている。
通常プロセスはコアイメージと呼ばれるアドレス空間を保有し、それに対応するプロセステーブルがあり、そこに各種情報が格納されている。
プロセス間通信
プロセスは自身がプロセスを生成でき(子プロセスと呼ばれる)、また子もプロセスを生成できるので木構造になる。あるジョブに協調、関連を持ったプロセスは相互に交信し、動作について同期を取る。この交信がプロセス間通信と呼ばれる。
1.3.2 ファイル
オペレーティングシステムはディスクやI/Oデバイスの固有の特性を隠蔽することが役目であり、ファイルは装置に依存しない抽象化されたモデルを提供する。ファイルはディレクトリによりグループ化され、階層構造をなす。
ファイルを読み書きする際に、まずオープンする必要がある。アクセスが正当ならばシステムはファイルディスクリプタと呼ばれる整数を返し、その数値でその後の処理を継続する。
スペシャルファイル
I/Oデバイスをファイルとして利用するための機構。
- ブロックスペシャルファイル
ハードディスクのようにランダムなアドレスを指定して参照するブロックの集合からなるモデルの装置に使われる。データ転送をブロック単位で行う。 - キャラクタスペシャルファイル
切れ目ないストリームでデータ転送を行う。キーボードやプリンタ。UNIXセマンティクスではスペシャルファイルは/dev
上にある。
1.3.3 シェル
OSはシステムコールを実行するコードであり、コマンドインタプリタであるシェルはOSの一部ではない。
1.4 システムコール
近代的なOSはPOSIXに準拠し、ほぼ同様のシステムコールを備えている。システムコールを実行する実際のメカニズムはマシンに強く依存する場合があるので、アセンブリコードで書かれる場合もある。そのため、C言語からシステムコールの利用を可能にするライブラリがまま用意される。
いかなる単一CPUでも一時的には一つの命令しか実行できない。もしプロセスがプログラムを実行しているときに、ファイルからデータを読み込みたいというようなシステムのサービスを必要としたら、OSに制御を渡すために割り込みを発生させる命令を実行する必要がある。OSはパラメータを分析し、プロセスが必要とするサービスを明らかにする。そのあとにシステムコールの処理を行い、システムコールを実行した命令に処理を戻す。
1.4.1 プロセス管理のシステムコール
fork
forkはMINIXにおいて新しいプロセスを生成する唯一の機能だ。forkを実行したプロセスの内容全て(ファイルディスクリプタ、レジスタなど)をコピーし、子プロセスを生成する。forkの返り値は子プロセスではゼロ、親プロセスには子プロセスの識別番号であるPID(Process Identifier)が入り、親子両プロセスは自分が親か子か識別できる。
waitpid
子プロセスの完了を待つ。端末からコマンドを読み込むと子プロセスを生成し、そのコマンドを子プロセスに実行させ完了を待ち、子の処理完了後に次のコマンドを読み込む。子プロセスの完了を待つために親プロセスはwaitpidを実行し、子プロセスの完了まで自プロセスを待機状態にする。
execve
プロセスのプログラム内容を更新する。第一引数に撮ったファイルをメモリに読み込み、コアイメージを新たにし、親とは違うプログラムを実行する。
fork, waited, execveを使った簡単なシェル
#define TRUE 1 while (TRUE) { /*永遠に繰り返す*/ type_propmt(); /*プロンプトの表示*/ read_command(command, parameters); /*端末からの入力の読み取り*/ if (fork() != 0) { /*親のコード 子プロセスの完了待ち*/ waitpid(-1, &status, 0); /*-1は任意の子プロセスを指定*/ } else { /*子のコード*/ execve(command, parameters); } }