ぐだぐだ低レベルプログラミング(240)x86(16bit)、286タスクステートセグメント

Joseph Halfmoon

前回ついにプロテクト・モードに突入。まずは16ビット世界の80286のプロテクト・モードからです。386以降の世界でも存在はしているけれど黒歴史。しかし8086からみたらめくるめく新たなセグメンテーション世界が存在しているのでした。そのメモリ空間を説明するのに避けて通れぬのがTASKです。OSの一部がCPUに載ってる?

※「ぐだぐだ低レベル プログラミング」投稿順indexはこちら

※実機動作確認(といってもエミュレータなんだけれども)には以下を使用させていただいております。

    •  Windows 11 PC (i5-1235U)
    •  Ubuntu 24.04 LTS on WSL2
    •  QEMU 8.2.2
    •  FreeDOS 1.3
286プロテクト・モードにおける「TASK」

近代的なオペレーティングシステムの多くが、プロセスおよびスレッドという単位でプログラムの実行を管理しているんでないかと思います。マルチコア使った高速化のときはスレッド使うのがフツー。スレッドのそれぞれがCPUの実行イメージを持ち、時分割なのか並列処理かは別にして独立に実行されますが、メモリ空間は共有。一方、プロセスという場合、それぞれ独自のメモリ空間を持っていて独立、相互に干渉できなくなっていたりもします(通常、1個のプロセスの中に複数のスレッドがある。)

286のタスクというものは、以下のように、ぶっちゃけプロセスに近い存在です。ただし、通常のプロセスはOSがソフトウエア的に実現しているのに対し、286のタスクはCPU自体がハードウエアで(実際にはCPU内部のマイクロコードで)処理している機能です。

    1. 1個のプログラムから見えるメモリ空間はタスク内で定義、異なるタスクは異なる空間が見える
    2. CPUの実行イメージ(ソフトから見えるレジスタ)はタスク内に保存されていて、タスク・スイッチ発生により自動的にセーブ、ロードされる
    3. 1個のタスク内での特権レベルの遷移をサポートするため複数の特権レベルに対するスタック情報をタスク内に保持できる
    4. タスクからタスクを呼び出し、再び呼び出し元のタスクに戻るといった運用を可能とするために呼び出し元タスクへのリンクもタスク内に保持できる

それを拙い図にしてみたものが以下に。TaskDiagram

前回、メモリを管理するためのデスクリプタ・テーブルというものがある、と説明しました。そのうちの2つは以下の通りです。

    • GDT=Global Descriptor Table、システムに一つしかない。OSなどの共有されるべきリソースを全てのタスクからアクセスするために存在する
    • LDT=Local Descriptor Table、タスク毎に一つ。タスクを切り替えれば自動的に別なテーブルが見える。各タスク毎のローカルなリソースをここに登録する。

つまりTASKというものを作れば、それぞれに別な「空間」を作ることができ、必要に応じてそのローカル空間は他のタスクから隔離することができるということであります。

そしてそのようなタスクの管理を286は自動でやってくれます。その際、TASKを定義するTask State Segment(TSS)という特殊なセグメントが存在し、TASKスイッチなどが起こる度にCPUがそれを勝手に読み書きしているというわけです。

Task State Segment(TSS)のレイアウト

TSSもセグメントである以上、多分GDTのどこかに登録されている筈です。ただし通常のデータ・セグメントとは異なり、セグメント内部の一部アドレスはCPUの使用のために予約されてます。その様子が以下に。TSSlayout

一番上の緑のところはLDTセレクタです。LDTはテーブルですが、これもまたセグメントの一種です。286は徹頭徹尾セグメンテーションなマシンです。複数のLDTがあり得るので、LDTを定義するデスクリプタがGDTのどこかに置かれている筈。ここにはそれを指すLDTセレクタを格納してあるはず。

黄色の部分がCPU内のレジスタイメージです。TASKスイッチが起こると286CPUは前のTASKのTSSのこの部分に現在のレジスタどもを全て書き出し、新たなTASKのTSSのここにおかれたレジスタイメージをロードして実行します。タスク切り替わったじゃん。

その下の青の部分は、プロテクトモードの特権のためのスタックの切り替えの仕組みが使っている部分です。通常のプログラムでもフツーは低い特権で実行しておいて、クリティカルな部分のみ高い特権に遷移して処理してくれるサービスをお願いする、というようなことを行うと思います。このような時に、低い特権も高い特権も同じスタックを使っていたのでは保護がしり抜けになってしまうので、特権遷移はスタックの切り替えも自動でやってくれることになってます。プロテクト・モードの特権についてはまた後日ですがな。なお、特権が一番高いのは「0」、低いのは「3」で4階層あります。

最後の一番下のフィールドは他のタスクから呼び出しされた場合、「他のタスク」を覚えておくためのフィールドです。

286は、タスクスイッチなどという、本来オペレーティングシステムがえっちらおっちら行う作業を「1命令」にて実行できます。当然、その作業の手順は長いっす。そのような1命令で数百クロックもかかることもざらです。モノホンのCISCマシンよな。そしてソフトウエア側の人々がいらんお世話だと思うような機能までキメウチ。全部受け入れて飲み込むか、知らんふりで通り過ぎるか。まあ、モダンな人々は286のセグメンテーションを「通り過ぎる」のだけれども。

そしてタスクスイッチには「タスク・ゲート」なんどという「ゲート」が登場します。「なんちゃら」ゲートはタスクだけではありません。インターセグメントの制御転送命令には特権もからんで奇怪なデスクリプタどもが幅をきかせてます。さらに、割り込み、例外をハンドルするための第3のデスクリプタ・テーブル、IDTも登場。286セグメンテーション、奥が深すぎる気がしないでもないんだが。

ぐだぐだ低レベルプログラミング(239)x86(16bit)、286プロテクトモード へ戻る

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です