1節
・486はCPUの一種 ・CPUの能力をオペレーティングシステムが引き出す。 ・オペレーティングシステムがCPUとソフトウェアを結びつける。 ・ms-dosは16bitcpuである8086を扱うために設計されたオペレーティングシステム ・486にはリアルモード(ms-dosを使える)、プロテクトモード(windowsを利用できる)、仮想8086モード(windwosを起動させたまま,ms-dosのアプリケーションを使用できる。これによってwindowsのメモリ管理といった拡張機能を利用しながらms-dosを使える)の3つのモードがあるが、これは広く普及している8086との互換性を取るために存在する。 ・8086の64kバイトの壁と640kバイトの壁:メモリのアドレスを16bitで指定するから0~65535(64kバイト)までしか連続で扱えない。よって配列の大きさに制限がある。640kバイトの壁は合計で640kバイト以上のメモリを扱えないという意味。2節
・486の性能向上に寄与した機能としては、キャッシュメモリ、浮動小数点演算機構、ページング機構、セグメント機構などがある。 ・キャッシュメモリは、メモリとCPUの中間に置く高速メモリ。高速だからクロック速度の高速化を存分に活かせる。最近使われたデータだけがキャッシュメモリには保存される。キャッシュ上に保存されてるメモリに書き込みを行うのと同時に、本来のメモリにも書き込みを行う。 ・本来のメモリを使わずに、キャッシュメモリを使えることをキャッシュにヒットするという。プログラム中の繰り返し処理を行っている部分では、最初の1回目の実行でプログラムがキャッシュメモリに保持されるので残りの繰り返し処理は高速化される。一方直線的に実行されるプログラムや大きな配列を連続的にアクセスする場合はキャッシュのヒット率が下がる。 ・クロックダブラーはクロックの周波数を倍にする。macとは1秒に2億回くらいの信号。クロックの信号を早くすると他の機器の反応速度も早くしないといけなくて金がかかるので、クロックダブラーはCPU内部のクロック速度だけ2倍にする。(だから外部メモリにアクセスするときとかは、クロックダブラーの機能を活かせないが、CPU内部のキャッシュメモリにアクセスしているときは機能を生かせる。) ・処理速度が高速だとその分バッテーリの消費が激しい。 ・486は基本機能を、マシン語命令をメモリから読み出し、CPU内部のレジスタを操作することで制御する。またシステム管理機能もシステム管理機能と結びついたレジスタを操作することで制御する。 ・一般命令はメモリやレジスタ間のデータ転送、加算といった命令。通常の方法でプログラムを作成する限りは一般命令以外は使わない。特殊命令はCPU内のシステムレジスタの設定を変更したり、参照したりする命令。プロテクトモードで動作するオペレーティングシステムでは内部で特殊命令を実行することでメモリ管理やタスク管理を行っている。 ・リアルモードでは特殊命令の一部しか使えない。 ・486のレジスタ群は4つ。一般レジスタは全てのソフトウェアが使用する。浮動小数点レジスタは浮動小数点演算を行うプログラムが利用する。システムレジスタはオペレーティングシステムが利用するレジスタ群。デバッグレジスタは、プログラムの実行を指定した条件で中断させ、プログラムの実行状況を確認するために使用。 ・オペランドサイズ=扱える数値の範囲。アドレスサイズ=扱えるアドレス空間オフセットアドレス。 ・データバス=コンピュータ内部で装置間を結ぶ共用のデータ伝送路。32bitならバスが32本。 ・8086用のプログラムを実行するときは486の32bitレジスタの下位16bitを利用すれば良い。3節
・オペレーティングシステムの役割は、アプリケーションソフトウェアなどの動作を支援すること。オペレーティングシステムの重要な機能は以下の4つ。「プロセス管理」は同時に複数のプログラムを並行して動かすのを管理する。「メモリ管理」はプログラムの要求に応じてメモリを割り当てる。「ファイルシステム」はディスク装置などの記憶媒体の管理を支援する。ハードウェアとしてのディスク装置は番号によってデータを呼び出す仕組みになっているが、ファイルシステムの機能によって、ファイル名で呼び出したり、ディレクトリ階層の形に整理しておくことが可能になる。「入出力管理」はさまざまな周辺機器を接続し、アプリケーションソフトウェアから利用できるようにする機能。周辺機器のコントロール方法は同じ種類の機器であってもメーカなどによって異なるが、機器に固有の制御をプログラム化した「ドライバ」というソフトウェアを組み込むことで、アプリケーションソフトウェアからはどの機器も同じ方法でコントロールできる。OSはドライバとアプリケーションソフトウェアの間を取り持つ。 ・タスク切り替えの方法は、まずタスクの状態(=CPU内のレジスタの内容)とOSがタスクを管理するのに必要な情報をメモリに保存。タスクを再開するときにはレジスタにメモリの値をセットする。 ・入出力の待ち状態になったタイミングでタスクを切り替えていくことで全体の処理効率が向上する。 ・イベント駆動(擬似マルチタスク)とは、キーボードとかユーザが操作するものが入出力れたタイミング(イベント)でタスクを切り替える手法。ただしこの場合はイベントが完了するまで他のアプリが停止してしまうという問題がある。 ・これに対し、入出力のタイミングだけでなく、一定時間がたつと強制的にタスクを切り替える方法をプリエンプティブなマルチタスクという。 ・メモリ割り当てはアプリからの要求に応じてメモリを割り当てたり回収したりする機能。 ・メモリ保護はプログラムからアクセスできるメモリ領域を制限できる機能 ・仮想記憶は実際に搭載されてるメモリよりも多くのメモリ領域があるように見せる機能 ・メモリ保護や仮想記憶の機能を実装するにはMMUというメモリ管理ユニットが必要。 ・マシンに搭載されているメモリのうち、OSが使用する部分を「システム領域」残りのメモリは「ユーザー領域」と呼ぶ。 ・マルチタスクのオペレーティングシステムでは、複数のタスクにメモリを配分しないといけない。これを管理するのがメモリ管理機能の役割。(シングルタスクなら必要ない) ・タスクとして実行されるアプリケーションソフトウェアは、プログラムを実行するために追加のメモリが必要であれば、OSにメモリ割り当てを要求し、OSは空きメモリからメモリを割り当てる。 ・アプリケーションソフトウェアは割り当てられたメモリが不要になればOSに返却する。またアプリが終了するとメモリを全て返却。 ・メモリ保護によって、あるプログラムが他のタスクやシステム領域に誤って書き込むのを防ぐ。 ・メモリ保護機能を実現するのが「アドレス変換機能」。p76。プログラムがアクセスするメモリアドレスと実際にメモリチップにアクセスするアドレスを変換してしまうことがアドレス変換。これによって各タスクは他のタスクを意識することなく全てのアドレスを自分のメモリであるかのように扱える。 ・MMU(メモリ管理のためのハードウェア)がアドレスを変換する。MMUはプログラムから送られてきたアドレスを内部の変換表に従って変換する。そしてこれをメモリに送る。MMUのアドレス変換表を作成するのはOSの仕事。 ・MMUを通過する前のアドレスを論理アドレス、通過した後のアドレスを物理アドレスという。 ・論理アドレスと物理アドレスの対応を設定することを「メモリを設定する」と言い、対応状態のことを「メモリマッピング」という。 ・OSがタスクを切り替えるときはタスクの状態の保存、復元と同時にMMUに保存されてるアドレス変換表も保存・復元する。 ・MMUによるアドレス変換には他にもメリットがある。それがタスクのアドレス空間を常に連続した領域のように見せられること。アドレス変換をしない場合は複数のタスクがメモリの要求と変換を繰り返すとメモリの割り当て状態が断続的になる。これでもOSはメモリの割り当てを管理することはできるが、アプリケーションにとっては問題が出てくる。その問題は連続した大きなメモリ領域が欲しくても割り当ててもらえないこと(でかい配列とかを扱えない)。 ・MMUの機能の中でも画期的なのが仮想記憶。実際に搭載されているよりも多くのメモリがあるように見せる。MMUのアドレス変換表(p89)には論理アドレス、物理アドレス意外にも存在確認欄が用意されてる。存在確認欄がONの時にはその論理アドレスには物理のアドレスが割り当てらている。OFFの時は割り当てがない。もしもアプリケーションソフトウェアが割り当てのないアドレスにアクセスを要求したら、MMUはCPUの実行ユニットに割り込み信号を送る。この割り込みによってアプリの実行は中断され、OSのメモリ管理モジュールが呼び出される。OSはあまりアクセスされてない物理アドレスを探し、その物理メモリの内容をディスク装置に格納する。 ・OSがプロセス管理やメモリ管理をする時は内部で特殊なマシン語命令を実行する。もしもこうしたマシン語命令をアプリケーションソフトウェア内で不用意に実行してしまうと、OSの管理している状態と食い違いが生じて危険。 ・OSは周辺機器の制御も行うが、もしもアプリが勝手に周辺機器にアクセスすると管理状態と食い違いが生じる。 ・そこでCPUの状態を特殊命令が実行できる特権状態と、それらを実行できない一般状態の2つに分離し、アプリは一般状態で実行する。486は4段階の特権レベルを持っている。 ・アプリ内で処理の条件を間違っていて何も反応を返さない無限ループに陥ったりすると、ms-dosではシステムをリセットするしかなかったが、マルチタスクの環境ではそれは許されない。 ・OSの「保護機能」はこうした状況でシステム全体の動作が不安定になるのを防止する。例としては前の章のメモリ管理における保護機能、1つのプログラムがシステム資源を独占して動き続けることのないようにタスク機能によってプログラムを切り替えることも保護機能の一つ。4章
・リアルモードからプロテクトモードに移行するにはコントロールレジスタ0(CR0)の最下位ビットの値を1にすれば良い(0にすると逆にリアルモードに切り替わる) ・準備段階を含めたプロテクトモードへの移行手順は、セグメントの設定→割り込みの設定→アドレス制限の解除→プロテクトモードへの移行となる。(5章、7章で詳しく説明) ・リアルモードに戻るときも準備段階が必要。じゃないと、CPUの内部状態がプロテクトモードの状態を引き継いでしまう。リアルモードに戻る方法は以下の通り。割り込みの設定→セグメントの設定→リアルモードへの移行→アドレス制限の設定 ・プロテクトモードに移行したらまずJMP命令を実行。なぜなら486はパイプライン処理を行なっており、マシン語プログラムの読み出し、解釈、実行を並行して行なっている。つまりある命令を実行している間に次の命令の解釈や読み込みが同時に行われている。よってパイプラインの中にある命令にはプロテクトモードに移行したにもかかわらず、リアルモードで解釈されたものがある。よってJMP命令でパイプラインを一旦無効化する。リアルモードに移行するときも一旦パイプラインを無効化する。5章セグメント
・セグメント方式とはメモリを区画割りする方式の一種。割り当てられた区画を「セグメント」という。 ・8086のセグメント方式にはいくつかの欠点があった。一つはセグメントの大きさを64kバイトより大きくできないこと。二つ目の問題は区画に柵がないこと。その為誤って別の区画にアクセスしてしまうことがあった。 ・一方486のプロテクトモードのセグメント機構では、区画の大きさに制限はなく、柵もついている。 ・セグメント方式によるメモリの指定方法を説明する。CPUとメモリはアドレスバスという信号線で結ばれている。メモリにアクセスするときは物理アドレスをアドレスバスに出力する。このアドレスは全メモリの先頭から連続して数えた通し番号で、1つ1つのメモリにつけられた背番号のようなもの。p122。 ・CPUの外のハードウェアでは、このようにメモリを物理アドレスで指定するが、CPU内部で実行するマシン語プログラムでは、物理アドレスでメモリを指定することはできない。 ・マシン語命令では「セグメントアドレス」と「オフセットアドレス」の組によってメモリを指定する。オフセットアドレスはセグメントの先頭から数えた通し番号のこと。 ・セグメントと物理メモリを結びつけるのが、CPU内部のアドレス変換回路(MMU)で、セグメントアドレスとオフセットアドレスの組を「リニアアドレス」と呼ばれるアドレスに変換。変換の手順はまずセグメントアドレスをセグメントアドレスをセグメント先頭のリニアアドレスに変換する(この後詳しく変換手順を解説)。このアドレスを「セグメントベース」という。次にセグメントベースにオフセットアドレスを加え、指定されたメモリのリニアアドレスを算出。9章で説明するページング機能をONにしなければリニアアドレスはそのまま物理アドレスとしてアドレスバスに出力される。 ・マシン語プログラムでは「セグメントアドレス」と「オフセットアドレス」の組によってメモリを指定するが、命令ごとにいちいち2つのアドレスを指定するわけではない。セグメントアドレスの方は最初に「セグメントレジスタ」にセットしておき、メモリをアクセスする個々の命令ではオフセットアドレスだけを指定。 ・セグメントレジスタはCS,DS,ES,SS,FS,GSの6つのセグメントレジスタがあり、役割が異なる。 ・マシン語命令の実行は、マシン語命令の読み込み→命令の解釈→命令の実行という流れ。p126.以下で流れごとのセグメントレジスタの役割を解説。 ・CSレジスタの役割は、マシン語命令を読み込むセグメントを指し示すこと。この時のオフセットアドレスはIPレジスタに入っていて、命令読み込み後IPレジスタの内容は次の命令を示すように加算される。 ・メモリの解釈はCPU内部で行われ、メモリアクセスは発生しないのでセグメントは関係ない。 ・DSレジスタの役割は命令の実行を行うセグメントを指し示すこと。 ・マシン語命令の実行によるメモリアクセスにDSレジスタ以外のセグメントレジスタが使われる場面がいくつかある。その一つがスタック領域へのアクセス(push,pop etc)。この時はSSレジスタのさすセグメントがアクセスされる。SSレジスタの役割はスタック領域として使うセグメントを表すこと。 ・マシン語命令の実行によるメモリアクセスは、SSレジスタを使う場合を除き原則DSレジスタを使う。この原則を変更するのが「セグメントオーバライドプリフィックス」。これは次に実行される命令で使われるセグメントレジスタの種類を変更する働きを持っている。これによってDSレジスタが表すセグメント以外にもう別のセグメントをアクセスしたい時にESレジスタ、FSレジスタ、GSレジスタ(特定の用途が決まってない予備のセグメントレジスタ)を使える。 ・セグメントアドレスとセグメントベースを対応させる方法はリアルモードとプロテクトモードで異なる。まずはリアルモードの場合を解説する。 ・リアルモードのセグメントアドレスはセグメントベースと固定的に対応している。具体的にはセグメントアドレスをそのまま4bit目から19bit目まで当てはめて、残りのbitを0にした数値がセグメントベースになる。このようにリアルモードではセグメントアドレスとセグメントベースの対応は固定的であるセグメントは必ず決まったセグメントベースに対応する。 ・リアルモードにおけるセグメント方式の欠点は以下の通り。まずセグメント先頭の物理アドレスの下位4bitは必ず0になるので、セグメントは必ず16の倍数のアドレスから始まらないといけない。それは良いとしてp130から分かるようにセグメントベースの20bit目から上は常に0になるので20bitで表せる範囲(1Mバイト)より大きなアドレスをセグメントベースとするセグメントを扱うことができない。だから2mバイトといったメモリを搭載していてもリアルモードでは1mバイトまでしかアクセスできない(1mバイトの壁)。 ・またセグメントの大きさは全て64kバイトで固定。よって他のセグメントと一部重なりあう状況が生まれる可能性がある。p132。またリアルモードではセグメントレジスタに任意の値をセットできるので、任意のアドレスにアクセスすることも可能という問題がある。 ・プロテクトモードのセグメント機構の説明を始める。 ・リアルモードではセグメントレジスタにセグメントアドレスをセットするだけでセグメントを使用できるようになるが、プロテクトモードではその前にメモリの割り当てを決める→ディスクリプタテーブルを作る→GDTRにディスクリプタテーブルのアドレスをロードするという3つの手順を踏まなければならない。 ・「ディスクリプタテーブル」とは「ディスクリプタ」と呼ばれる一種の設計図を収納しておく整理棚。ここにはセグメントの設計図やタスク管理の方針を示した設計図などいろんな種類のディスクリプタを保存。 ・リアルモードではセグメントレジスタにセットする値のことをセグメントアドレスと呼んでいた。プロテクトモードではセグメントレジスタにセットする値のことを、「セレクタ値」という。 ・リアルモードではセグメントアドレスとセグメントベースとの関係は固定的に決まっていたが、プロテクトモードでは「セレクタ値」と「セグメントベース」との間に固定的な関係はない。つまり、セレクタ値の大小は物理メモリ上の配置とは関係しない。またセレクタ値に対応するセグメントが存在しないことさえある。 ・リアルモードのセグメントアドレスは16bitの大きさを持ち、セグメントベースの値に応じて0000~FFFFまでの値をとる。一方プロテクトモードのセレクタ値も16bitの大きさを持つが、任意の値を利用できるわけではない。まずセレクタ値のうち、0はセグメントレジスタを無効にするために使われる。つまりセグメントレジスタに0をセットすると、そのセグメントレジスタを使ったセグメントのアクセスはできなくなる。有効な値としては0008,0010,0018と8つおきに飛び飛びの値を使う。 ・8つおきの理由は、その間の0001~0007とかにも役割があるから。(10章で解説) ・セレクタ値とリニアアドレスの対応は「セグメントディスクリプタ」に設定しておく。アドレス変換回路はセグメントディスクリプタから情報を読み出すことで、セレクタ値をセグメントベースに変換する。そしてオフセットアドレスを加算してリニアアドレスを算出。セグメントディスクリプタはセグメントベース、セグメントの大きさを表すリミット値、セグメントの属性を表す数値の3種類の情報からなる(詳しくは後述)。 ・セグメントディスクリプタはセレクタ値の順番にディスクリプタテーブルに収められているp139。ディスクリプタテーブルはCPU内部ではなくメモリ上にあるディスクリプタテーブル用のセグメントに設置。 ・今解説しているのはグローバルディスクリプタテーブルのこと(システム中に一つだけ存在するディスクリプタテーブルで、全てのプログラムから共通して参照されるセグメントを定義するためのもの)。他にもタスク一つ一つにつき存在するローカルディスクリプタテーブルなどが存在する(別の章)。 ・OSが486のセグメント機構を使ってメモリ管理を行う方法を今から解説。 ・OSがメモリ割り当ての要求を受け取ると、メモリの空いている領域を探して確保。次にディスクリプタテーブルの空いている引き出しを確保。この引き出しのセグメントディスクリプタに確保したメモリの領域のセグメントベースなどの情報を設定。そしてこの引き出しの番号、すなわちセレクタ値を要求したプログラムに返す。OSを呼び出したプログラムは帰ってきたセレクタ値をセグメントレジスタにセットし、メモリをアクセス。プログラムがメモリをアクセスすると486はセグメントディスクリプタの情報を取り出してアドレス変換を行い、その結果プログラムは目的のメモリにたどり着ける。 ・セグメントディスクリプタの中身を解説。「セグメントベース」の部分にはセグメント先頭のリニアアドレスが入っている。「リミット値」の部分にはセグメントの大きさ−1が入っている。これはオフセットアドレスとして許される値の最大値を意味する。リアルモードではセグメントの大きさを64kバイト未満にしてもその範囲を超えてアクセスしてしまうことを制限できなかったが、プロテクトモードではリミット値を設定しておくことで、この大きさを超えるオフセットアドレスによるアクセスを防止できる。「属性」の部分にはセグメントの種類などの情報を入れておく(詳しくは6章の保護で解説)。 ・CPUにアドレス変換を行わせるためには、あらかじめディスクリプタテーブルのアドレスを設定しておかなければならない。これはOSがシステム起動時に「GDTR(グローバルディスクリプタテーブルレジスタ)」を使って行う仕事。このレジスタにディスクリプタテーブルのリニアアドレスをロードしておくことで、CPUがディスクリプタテーブルを参照できるようになる。 ・セレクタ値とオフセットアドレスの組からリニアアドレスへの変換は、命令の読み込みや実行の度ごとに行われる。つまりその度ごとにセグメントディスクリプタ内の「セグメントベース」が参照されていることになるが、そのために毎回メモリ内のディスクリプタテーブルにアクセスしているわけではない。CPUのセグメントレジスタには、セレクタ値を入れる部分以外に、セグメントディスクリプタを入れておくための「セグメントディスクリプタキャッシュ」と呼ばれる部分がある。セグメントレジスタにセレクタ値をロードすると、自動的にセグメントディスクリプタテーブルからそのセレクタ値に対応するセグメントディスクリプタがセグメントディスクリプタキャッシュにロードされるという仕組みになっている。 ・486はリアルモード下でも、32bitの「オペランドサイズ」も16bitのオペランドサイズ(マシン語命令で扱う数字の大きさ)のどちらも使える。 ・アドレス空間の大きさを「アドレスサイズ」という。486はオフセットアドレスに32bitの値を使うことができるので、1つのマシン語命令でアクセスできるアドレスサイズは32bit。 ・32bitのアドレスサイズやオペランドサイズはプロテクトモードでなければ有効に活用できない。 ・オペランドサイズが32bitの命令の先頭についたマシン語命令のことを「オペランドサイズプリフィックス」という。 ・リアルモードではオペランドサイズやアドレスサイズのデフォルト値は必ず16bitになる。プロテクトモードでは「サイズプリフィックス」を使用することで16bitか32bitかを示す。16bitか32bitかはセグメントディスクプリタ中の「属性」のDbitできまる。6章 保護
・プロテクトモードでは一つ一つのマシン語命令を実行するたびに、その命令を実行していいかをCPU内でチェック(セグメントの属性、セグメントリミット、特権レベル、タスクが合っているかp191)し、事故である可能性を発見すると処理を中断してフォールト(7章)を返して、OSに制御を移す。 ・セグメントの属性について。 ・セグメントの属性はセグメントディスクリプタの一部としてOSがセットする。属性フィールドは1つ1つのbit単位で独立した意味を持っている。p193。sbitが1だとセグメントを表すが、0だとゲートとかを表す。 ・属性フィールドは他にもセグメントが物理メモリ上に存在するかを表したりする。Abitはそのセグメントが最近アクセスされたかを表す。これが仮想記憶の実現に役立つ。 ・セグメントの種類は「コードセグメント(CPUで実行するマシン語プログラムを入れておく。CSレジスタが指す)」と「データセグメント(CSレジスタ以外が指し示す)」の二つに主に分類できる。コードセグメントには通常のコードセグメントとコンフォーミングセグメントの2種類がある。それぞれについて「実行専用」と「実行及び読み出し可能」の属性がある。(10章)実行専用セグメントはデータセグメントとしてのアクセスはできないが、実行及び読み出し可能のコードセグメントはデータセグメントとしてのアクセスも可能。 ・最後に残った2種類?のスタックセグメントはSSレジスタが指し示す。 ・プロテクトモードのプログラムを実行しているときは、0~3までの動作レベルがあり、それぞれの数値は特権レベルと言われる。レベルの数値が低いほど「特権レベル」が高い。 ・動作レベルは実行できる命令の種類とアクセスできるメモリの範囲を指定。レベル0は特権命令を実行でき、全てのメモリに読み書きが可能。アプリケーションソフトウェアを実行するときは動作レベル3。特権レベル1や2は、OSの補助的な機能を担当するプログラムやハードウェアを操作するプログラムなどを動かす。 ・セグメントはそれぞれの特権レベルを持っている(セグメントディスクリプタの属性の「DPLフィールド」で規定)。 ・プログラム実行中の動作レベルのことを「CPL(current privilege level)」という。CPLはセグメントの特権レベルで決まる。 ・動作レベルを変更するためには、セグメント間CALL命令やJMP命令を実行。その際はコールゲートを通る。 ・動作レベルよりも高いレベルを持つセグメントをアクセスすることはできない。動作レベルの値はCSレジスタに保存。セグメントアクセスのチェックはセグメントレジスタにセレクタ値をロードする際に行われ、セレクタ値の指す特権レベルが動作レベルよりも高い場合はフォールとが起きる。p202 ・I/Oポートの特権レベルをIOPLという。OSだけがIOPLを変更できる。 ・動作レベル移行時の保護について解説する。 ・「コールゲート」を介して上位の特権レベルに移行する。これの典型的な用途はOSの呼び出し口。アプリケーションソフトウェアがコールゲートを呼び出すことで、OSが呼び出される。 ・OSは起動時にコールゲートを作成する。コールゲートは「システムオブジェクト(ディスクリプタによって定義されるもののうちセグメント以外のもの)」の一種。ディスクリプタにはセグメントディスクリプタ以外にもゲートディスクリプタがある。コールゲートはセグメントを呼び出すのと同じように呼び出せるp206 ・ゲートも特権レベルを持つ。動作レベル3の状態では特権レベルが3のゲートしか呼び出せない。ただしゲートが指すルーチンのセグメントの特権レベルが0であっても、ゲートの特権レベルが3であれば動作レベル3のプログラムから呼び出せる。p2117章 割り込み
・コンピューターは予め決められた手順に沿って処理を進める。しかし全ての処理の順序を事前に決めておくのは困難。例えばキーボード入力の処理では入力を検出したらそれを取り込むという手順になるが、人間がキーボードを操作するのは必ずしもコンピュータが入力を待ち受けている瞬間とは限らない。こうした状況を想定して効率よく処理を進めるための仕組みが「割り込み機能」。本来はタイミングの予測できない周辺装置の制御処理のために生み出されたものだが、メモリ管理や保護機能にも応用されてる。 ・割り込み機能の仕組みを一言で言うと、プログラムの実行を何らかのきっかけで中断し、一時的に他のプログラムを実行すること。処理の流れは以下の通り。割り込みの発生→割り込み処理のルーチンの呼び出し→割り込み処理ルーチンの実行→割り込み処理の完了。 ・割り込み処理が終了しても元のプログラムに戻らないことがある。例えば割り込みを利用してタスクの切り替えを行う場合は、元のプログラムには戻らず、中断していた他のタスクのプログラムを実行を再開。このように割り込みが発生した時に切り替えを行うことで複数のタスクを変わるがわる実行できる。また割り込みの要因が何らかのエラーの時はプログラムの実行を停止することもある。 ・割り込みは割り込み要因の種類によって「ハードウェア割り込み」「例外割り込み(マシン語命令を実行した結果、あるいは実行しようとした時点でプログラムの実行を継続できないような例外状態になったときに発生)」に分けられる。例外割り込みはトラップ、フォールト、アボートの3つに分けられる。 ・ハードウェア割り込みは486の割り込み入力端子に信号が送られてくることによって発生する割り込み。周辺機器に操作が加えられたことや、周辺機器での実行が完了したことを示す。流れは以下の通り。割り込みの発生(キーボードを押すとキーボードコントローラは割り込みコントローラに信号を送り、割り込みコントローラは486の割り込み入力端子に信号を送る。)→割り込み処理ルーチンの呼び出し(486は割り込み要因を受け取り、割り込み処理ルーチンを選び出して開始する)→割り込み処理ルーチンの実行(キーボードに対応する割り込み処理ルーチンでは、キーボードコントローラから押されたキーの情報を読み取り、割り込み処理ルーチンが管理するメモリ内に格納しておくという処理を実行)→割り込みの終了(割り込みコントローラに処理終了を通知する) ・割り込みコントローラは486に割り込み要因を伝える。コントローラーは割り込み要因に対応する割り込み番号を486に送る。割り込み番号は全部で256種類で割り込み処理ルーチンもその数だけ用意しないといけない。 ・486が割り込み信号を受け取っても、必ず割り込みが発生するわけではない。486内部のフラグレジスタには「割り込み許可フラグ」があり、ハードウェア割り込みの受付をコントロールする役割を持っている。割り込み許可フラグの値が0か1かで割り込みを受け付けるか受け付けないかが決まる。これによってプロテクトモードに移行する処理部分で、セグメントレジスタの設定が完了するまでは割り込みを禁止する。 ・例外割り込みについて解説する。 ・「トラップ」は「ソフトウェア割り込み」とも呼ばれる。ms-dosではアプリケーションソフトウェアがOSやBIOSを呼び出す方法として使われる。 ・「フォールト」はマシン語命令を実行しているときに検出された例外状態が割り込み要因となって発生する割り込みのこと。例えばオフセットアドレスがリミット値を超えていた時とか徐算エラーで起こる。このような種類のフォールトは主に保護機能に関係するもので、多くの場合そのプログラムの実行を続けることはできない。そのため割り込み処理ルーチンが実行された後は元のプログラムに戻ることなく強制終了させてしまう。 ・「アボート」はフォールトと同様にマシン語命令を実行しているときに検出された例外状態が割り込み要因となって発生する割り込みのことで、フォールトよりも致命的なエラーが起こった時に発生。例えばフォールトが発生して対応する割り込みルーチンの実行を開始したところ、割り込み処理ルーチンが無効なセグメントにあったりすると再びフォールトが発生することになる。このような場合にはアボートが発生する。 アボートが発生するとその原因を解明するのは難しいのでプログラムは強制終了させる。 ・プロテクトモードとリアルモードでは割り込みの扱い方がリアルモードとは全く異なる。まずはリアルモードを解説。 ・リアルモードでは割り込み番号と割り込み処理ルーチンの対応を「割り込みベクタテーブル」に設定。割り込みベクタテーブルには割り込み処理ルーチンのアドレスを割り込み番号の順に並べられている。割り込みが発生すると以下の流れで処理が行われる。CS、IP、フラグレジスタの内容をスタックに退避→割り込みベクタテーブルから割り込み処理ルーチンのアドレスをCS、IPレジスタにセット→CS、IPレジスタより示される割り込み処理ルーチンを実行→割り込みが終了するとスタックからCS、IP、フラグレジスタの内容を取り出す。 ・プロテクトモードでは割り込み番号と割り込み処理ルーチンを対応させるために、割り込みベクタテーブルではなく「割り込みディスクリプタテーブル」を使用する。割り込みディスクリプタテーブルはゲートディスクリプタを並べたもの。各ゲートは格納されている順序で割り込み番号を割り振られている。格納されているゲートには「割り込みゲート」「トラップゲート」「タスクゲート」の3種類がある。 ・割り込みの設定が適切に行われていない時に割り込みが発生したり、割り込みゲートの設定が誤っていて対応する割り込み処理ルーチンに制御を移せなくなったときは、486は「シャットダウン状態」に入る。シャットダウン状態では486の機能は停止し、プログラムの実行は続けられなくなる。この状態になると486は外部にシャットダウン信号を出力し、リセット回路を動かす。8章 タスク
・タスクとは独立した資源を保持するプログラムの実行単位のことです。「実行単位」とはプログラムが起動して処理を行い終了するまでの一連の実行の流れを指す。「資源」とはメモリ空間やファイルやデバイスの操作など。OSによってはタスクではなく「プロセス」という用語を使うこともある。 ・OSは内部のタスク管理処理の中で486のタスク機能を呼び出すことによってマルチタスクの実現を大幅に効率化することができる。486のタスク機能には「タスク切り替え機能」「メモリ空間の分離保護機能(10章)」の2つの働きが主にある。 ・タスクを切り替えるにはタスクの保有するメモリ空間とかの資源を全て切り替えないといけなくて大変。だからOSによってはタスクに加えて「スレッド」という概念を導入する。タスクを更に細分化したのがスレッドで、スレッド同士は同じメモリ空間を共有できて便利。 ・タスクの切り替えは以下のように行われる。タスクAの実行状態(すなわちCPU内のレジスタの内容をメモリに保存)→メモリに保存してあったタスクBの実行状態をレジスタに読み込み。 ・486はタスク状態の保存・復元に使用するデータ領域の構造をTSSという形式に規定。 ・TSSはタスク一つにつき必ず一つ用意しなければならない。タスク状態をTSS(メモリの一部)に保存。 ・タスクの切り替えは486とOSで作業を分担。486はCPUの状態の保存と復元を担当し、OSはそれ以外のタスク状態の管理をする。こうした分担を容易にするためにTSSは「レジスタ保存部」「OS用タスク管理情報部」「I/O許可マップ」という3つの部分に分かれている。 ・レジスタ保存部は486自身が使用する。OSのタスク切り替えプログラムの中でタスク切り替えを行う486のマシン語命令を続行すると、486はその1命令でレジスタの内容の保存と復元を実行する。 ・タスク状態にはレジスタの内容以外にもファイルやデバイスの操作状況などの資源がある。これはOSごとに管理する内容が異なるのでTSSの2番目に位置するOS用タスク管理情報部に保存する。 ・I/O許可マップは保護機能に関係する部分で特定のI/Oポートのアクセスを許可するためのもの。TSSにI/O許可マップがない場合は、そのタスクはI/Oポートにアクセスすることができない。 ・タスクを起動する処理の手順を解説。TSSに加えてTSSディスクリプタを作成しないといけない。タスクを起動するたびにTSSとTSSディスクリプタが一つずつ増えていく。TSSディスクリプタはTSSのアドレスやサイズを記述していくディスクリプタのことで、セグメントディスクリプタと似た構造をしている。ディスクリプタテーブルの中にセグメントディスクリプタやゲートディスクリプタやTSSディスクリプタなどがまとめて詰め込まれている。 ・タスク切り替えの瞬間には二つのTSSが必要。一つは現在のタスク状態を保存するもので、もう一つは切り替え後のタスク状態を取り出すもの。タスク切り替えを実行するセグメント間ジャンプ命令では、ジャンプ先のセレクタ値として切り替え後のTSSを指定。現在のタスク状態はCPU内のレジスタである「TRレジスタ」で、そレは現在実行中のタスクが使っているTSSセレクタ値の値を格納する役割を持っている。486はタスク切り替えを実行するときにTRの指すTSSに現在のタスク状態を保存する。p277 ・TSSにも特権レベルがあり、一般にはTSSの特権レベルを0にしておくことによってタスク切り替えの処理をOSの内部でのみ実行するようにする。なぜならアプリケーションソフトウェアが勝手にタスクを切り替えるとOSがタスクの状態をうまく管理できなくなるから。 ・割り込みやフォールトの発生によって割り込みゲートやトラップゲートが呼び出される場合には、それだけではタスクが切り替わらない。p282.タスクは切り替わらずに同じタスクの中で割り込み処理ルーチンが実行される。コールゲートを介してOSを呼び出した時も同様。もちろん割り込みとタスク切り替えを連動させることも可能p2819章 ページング
・セグメント方式はメモリの割り当てやアドレス変換をセグメントを単位として行う。セグメント方式ではメモリを任意の大きさで割り当てる。一方メモリをページと呼ばれる固定長の単位に区分けする方式がページング方式。ページング方式ではメモリの割り当てやアドレス変換をページを単位として行う。 ・486ではページングを用いなくてもタスク1とタスク2で別々なセグメントを用いることで同様な効果を得られる。しかし11章の仮想8086モードではページングを用いてタスク間のメモリ空間の分離を実現する。 ・ページング方式は仮想記憶を実現するのに適したメモリ管理方式。 ・メモリアクセスには局所性がある。すなわちプログラム実行中のある短い時間をとってみると、アクセスしているメモリの量は限られており、しかもごく近い場所に集中しているという性質。プログラムの中のある区間で必要とするメモリは、全ページのうちのごく僅かに過ぎない。そのためその時点で必要な論理ページだけを物理ページに対応づけておけばプログラムの実行は可能。よって必要に応じてメモリを割り当てる方式なので「デマンドページング」と呼ばれる。 ・デマンドページング方式では、プログラムの起動時に実行ファイルから実行開始部分のページだけを物理ページに読み込んで論理ページに割り当てれば、すぐに実行を開始することができる。セグメントよりもページの方がサイズが小さいから読み込みにかかる時間は短い。 ・ページング方式の仮想記憶とセグメント方式の仮想記憶の手順はほぼ一緒。 ・セグメント方式とページング方式は一長一短がある。メモリ保護やタスク間のメモリ空間の分離には、属性や物理メモリとの対応を大きな区間単位で設定できるセグメント方式の方が適している。一方メモリアクセスの局所性を利用して効率的に物理メモリを割り当てたり、仮想記憶を実現するにはページング方式の方が適している。86系のCPUでは最初はセグメント方式だけしかなかったが、セグメントの大きさを32bitで表せる大きさに拡大したのでセグメント方式だけでは効率的なメモリ管理が困難になってしまい、ページング方式も導入された。 ・486では物理メモリ空間をまずページング方式によって固定長のページ単位に分解する。ページング方式によって物理メモリ空間に対応づける論理メモリ空間のことをリニアアドレス空間と呼ぶ。更にリニアアドレス空間のメモリをセグメント方式によって任意の大きさに分解する。p301.セグメント機構から見るとリニアアドレス空間はフラットな連続したアドレス空間のように見える。セグメントとページングは独立した機能として動くので、ページ境界とは無関係にセグメントに分割することができる。とは言っても1つのページが複数のセグメントに跨らないようにした方が管理は楽。 ・セグメント機構によってセグメントアドレスとオフセットアドレスの組はリニアアドレスに変換される。ページング機構がOFFの時はリニアアドレスがそのまま物理アドレスとして出力されるが、ONの時はページング機構が働き、リニアアドレスが更に変換された後のアドレスが物理アドレスとして出力される。 ・ページング機構ではまず32bitのオフセットアドレスを上位20bitのページ番号と下位12bitのページ内オフセットに分解。ページ内オフセットには変換を加えずそのまま物理アドレスの下位12bitとする。ページ番号の方は論理ページと物理ページの対応表であるページテーブルを使って物理ページ番号に変換。ページテーブルには、ページ機能をONにする前にあらかじめ各ページの対応を設定しておかないといけない。しかしこのままでは現実的でない。なぜならページテーブルの欄はページの数だけ用意しないといけず、そのページの数は2**20こもあり、メモリを圧迫するから。486ではページ番号を更に二つに分解することでこの問題を解決している。これによってページテーブルを細分化してページテーブルも通常のメモリと同様にページとして扱い、仮想記憶の対象にできる。 ・「PTE」は32bitの大きさで、ページング機構における一連のアドレス変換の手順のなかで、ページ番号、すなわちページ先頭のアドレスを取り出すための役割を持っている。それ以外にも以下のような役割がある。pbitはPTEに対応するページがメモリ上に存在するかを0,1で示す。abitはメモリが足りなくなった時に、OSが「ページアウト(あまり使われていない物理ページの内容をディスクに保存)」するページを選ぶために利用。dbitはそのメモリに書き込みが行われたかを判断する。行われてなければページアウトの際にディスクに書き込みをする必要がない。 ・PTEもキャッシュとして「TLB」というキャッシュメモリに保存される。メモリ上のPTEの内容はTLBにキャッシュとして保存されているが、メモリ上のPTEの内容を変更(論理ページと物理ページの対応を変更した時)した時にはTLBの内容とPTEの内容が食い違うため、PTEを変更した時はTLBを無効にする(「TLBのフラッシュ」)10章 セキュリティ
・486ではアプリケーションソフトウェアを実行している状態からコールゲート、割り込み、例外によってOSが呼び出されると、CPUの動作レベルは最低レベルの3から0に移行する。8章タスクで解説したようにこの場合は同じタスク内で動作レベルが移行するからアプリ実行状態をそのまま引き継ぐ。ここにセキュリティ上の危険地帯が潜んでいる。 ・OSはまずアプリから引き継いだセグメントレジスタなどの状態を保存する。そしてOS自身のセグメントをアクセスできるように、セグメントレジスタの設定を行う。ここで注意しないといけないのはセグメントの設定を行う前にレジスタの内容を保存しないといけないこと。アプリを実行中のデータセグメントやスタックセグメントは必ずしも適切に設定されているとは限らない。場合によってはアクセスした途端にフォールトが発生することがある。例えばスタックポインタがリミット値ギリギリを指していて、新たにデータをpush命令で保存することができないかもしれない(レジスタの内容はスタックに保存する)。もしそうなると連続でフォールトが発生してしまう。OSはこのような事態を避け、常に安全に動作しないといけない。 ・このような理由から486は特権レベルの移行と連動して必ずスタックポインタを切り替える仕組みになっている。p341。OSはアプリを起動する前に特権レベル0用のスタック領域を設定しておく。 ・スタック切り替えの仕組みとしては、切り替え前のSS,ESPレジスタの値を内部レジスタに保存↓特権レベル0のスタック領域のアドレスを現在実行中のTSSから取り出しSS、ESPレジスタにロード(スタックが特権レベル0に移行)→それまで内部レジスタに保存していた昔のESP、SSレジスタの値をスタックにpush→昔のCS、EIPレジスタの値をスタックにpush。特権レベルをもとに戻すときは、まずCS、EIPレジスタの値をスタックからPOP→486はPOPしたCSレジスタの動作レベルが現在より低いことを検知すると、更にスタックからSS,ESPレジスタの値をpop。これによってスタックの特権レベルがもとに戻る。 ・TSSには各特権レベルごとにスタック領域のアドレスを格納しておくフィールドがある。OSはタスクを起動するときにタスク用のメモリを確保するとともに、自分自身のためのスタック領域を確保する。そしてスタック領域のアドレスをTSSに設定。 ・トロイの木馬とかへの対策を解説。 ・486で考えられるセキュリティ破りの一例を紹介。セキュリティ破りを行おうとするPというプログラムがある。PはOSのセグメント0010の内容を読み出し、暗証番号を盗もうとしてる。しかしセグメントの特権レベルは0だから内容を直接読み出すことはできない。そこでOSの機能の一つであるwritefaileという処理したデータをファイルに保存する機能を使う。Pはコールゲートを使って動作レベルを0にした後、OSのセグメントの値をwritefileでメモリに書き込み、後からそのファイルの内容を読み出してしまう。 ・これを防ぐために486には「要求者特権レベル」のチェック機能が付いている。要求者特権レベルとはセグメントのアクセスにおいて、本来そのセグメントにアクセスしようとしたプログラムの特権レベルのこと。今回の場合だと0010にアクセスしたのはOSだが、OSはpの代理でアクセスしたわけだから要求者特権レベルは3。 ・要求者特権レベルがセグメントの特権レベルよりも低い時は一般保護例外を発生させる。 ・セキュリティの基本はガードをかけることだが、ガードが厳しすぎると処理に時間がかかる。例えばコールゲートは特権レベルを移行するたびにスタック間の引数のコピーが発生し、更にセグメントレジスタの再設定や、要求者特権レベルの設定を行わないといけない。486にはコンフォーミングセグメントと言って、セグメントの特権レベルとCPUの動作レベルが異なってもアクセスできるセグメントが存在する。 ・アプリ同士は同じ特権レベルだから、当てずっぽうでセレクタ値を指定すると他のアプリのセグメントにアクセスできる可能性がある。486はアプリのセキュリティを保つためにタスクと連動した保護機能がある。これは「ローカルディスクリプタテーブル(LDT)」によって実現されている。セグメントのディスクリプタやコールゲートを格納するディスクリプタテーブルには二つの種類があり、これまで解説してきたのは「グローバルディスクリプタテーブル(GDT)」だった。 ・LDTはGDTと同じ構造をしており、図10-17のようにテーブル内の各ディスクリプタがセグメントやゲートを表す。重要なのはGDTは全てのタスクに共通なのに対し、LDTはタスクごとに独立して存在していること。よって異なるタスクでは同じセレクタ値であっても対応するセグメントは全く別のものになる。よってプロフラムミスから他のアプリのセグメントを使ってしまうということは起こらない。 ・LDTをタスクごとに切り替える仕組みを解説。GDT内にLDTを指すディスクリプタを作成しておく。LDT内のセグメントにアクセスするときはまずGDTにアクセス→GDTからLDTを指すディスクリプタを取り出す→それをもとにLDTにアクセスという流れをとる。 ・タスク切り替えではLDTの切り替えが自動で行われるので、間違ってセレクタ値をセットしても他のタスクに割り当てられたセグメントにアクセスすることはない。 ・セグメントだけでなく、I/Oポートについてもアプリ、あるいはデバイスドライバソフトウェアなど、実行するプログラムごとに異なったアクセス権を設定しなければならない。このため486はタスクごとにアクセスできるI/Oポートを限定する機能を持っている。 ・「I/Oポートの仮想化」とはアプリのI/OポートアクセスをOSの管理下に置き、排他制御やエミュレーションを行うこと。p36511章 仮想8086モード
・OSにとって互換性は重要な性質。新しいOSの性能が凄くても従来のOS用のアプリが利用できないのでは不便。 ・386には「仮想8086モード」が組み込まれた。このモードはセグメントの扱いをリアルモードと同じにしながらも、プロテクトモードの一部として実行される動作モード。そのためメモリ管理や保護機能も使える。 ・仮想8086モードに移行するにはEFLAGSレジスタ中のVMビットを1にセットする。0にするとプロテクトモードに戻る。 ・仮想8086モードでは常に動作レベルは3のため、CPUのプロテクトモードに関連する状態を変更することはできない。 ・仮想8086モードではセグメント機構による保護機能は働かないが、p373のような保護機能が働く。 ・かそう8086モードではプロテクトモードのタスクも動作している。 ・タスク切り替えの際にロードされたEFLAGSレジスタのVMビットが1だと、タスク切り替えの直後から自動的に仮想8086モードに移行する。 ・仮想8086モードの本来の役割は、プロテクトモードのOS上でMS-DOS用のアプリを実行させること。しかしMSーDOSだけの環境でも仮想8086モードはメモリ管理システムに拡張を加えられるので役立つ。 ・メモリ空間はMS-DOS用のメモリとプロテクトモード用のメモリの2種類がある。以降はそれほど重要ではなさそう(主観)なので省略