見出し画像

80286のプロテクトモード - タスクって何?

以前に80286の仮想アドレスの変換の仕組みについての記事を書いたのですが、ほとんどの人はこの仕組みを知る必要はないのにも関わらず、思いの外多くの「スキ」が頂けてビックリしています。

80286のプロテクトモード - 仮想アドレスから実アドレスに変換する仕組み

確かに知る必要がないからこそ、あまり説明をしているものを見ることがないので目新しいところはあったのでしょう。さてアドレス変換が済んだので、現在の多くのOSがサポートしているタスク管理の仕組みも見ていきましょう。

ちなみにCPUがタスクをサポートする以前(サポートするCPUでも、そのモードを使っていない場合)は、複数のプログラムが同時に動作するように見えることはあっても、それらは同じメモリ空間を共有しており、OSも含めてひとつの大きなプログラムが互いの協調して複数のプログラムが動いている「ように」見えるだけで、いずれかのプログラムが正しく動作しなくなれば、OSも含めたシステム全体の動作がおかしくなり、再起動しなければ正常に動作しませんでした。

CPUがタスクをサポートしている場合、タスクごとに独立した仮想アドレス空間を持ち、敢えてメモリを共有しない限り、他のタスクのメモリを読み書きすることは出来ません。これでプログラムにバグがあったとしても、他のプログラムのメモリを書き換えたりすることは原理的に出来なくなるので安心ですし、もちろんOSが使っているメモリを壊されることも防げます。OSが生きているので問題のあるプログラムのタスクを外すということが出来るので、もう暴走するたびにシステム全体を再起動する必要は無くなる訳です。

プロテクトモード

さて、そもそも「タスク」とは何でしょう。これは何もコンピュータのプログラムに限った言葉ではなく何らかの作業(仕事)を指す言葉で、タスクを同時並行的に進めることを「マルチタスク」というのは聞いたことがあるかもしれません。タスクには状態があって、この状態を管理しつつ途切れ途切れであっても作業を進めていく手順が「タスク管理」と呼んでいたりするわけです。

タスク管理

マルチタスク (心理学)

ということで、人間もCPUも脳みそはひとつ(最近のCPUは必ずしもひとつではないですが)しか無いので、タスクとして処理を継続するためには、切り替えのたびにそれまでやっていたことをいったんどこかにすべて記録しておいて、他のタスクに切り替えるということをして全体として同時にいくつものタスクを実行しているかのように見せるわけです。

一般的なプログラムはメモリに読み込まれて実行される際に、この「タスク」として登録され実行されます。「タスク」は必要に応じて「中断」されたり「再開」されるので、「中断」の際には、その途中の状態を保存しておくためにCPUの内部状態すなわちレジスタの値をメモリに格納し、「再開」する時には、それを戻して実行を始めます。メモリに関してはタスクごとに別々のメモリが割り当てられているので、そのままで大丈夫です。この状態を格納するためのメモリを管理するためにセグメントを割り当てます。


タスクを切り替える際に保存されるメモリ領域(TSS - Task State Segment)

TSSへのバックリンクというのは、タスク全体を管理しているTSSへのポインタ、イニシャル・スタックというのは、タスク状態を保存するというのではなく、タスク内で他の特権レベルの遷移(他のプログラムを呼ぶときなどに起こる)の際に、より高い特権レベルのスタックを保護するためのものです。一番低いレベル3については保護する必要がないので、ここに保存されません。LDTは覚えていますよね?タスクごとの仮想アドレスのマッピングテーブルです。

Task state segment

そしてそれぞれのタスクは以下のディスプリクタによって管理されています。ディスプリクタは全部で8バイトあるのですが、それぞれのワードはインテルのエンディアンになっていることに注意してくださいね。

TSS ディスプリクタ

そしてTR(タスクレジスタ)に、このディスクリプタを設定すれば、この情報を下にカレントタスクが指定されるようになっています。このTSSベースのアドレスに先のレジスタ退避場所があるわけですね。これらがどういう関係になっているのかというと、

という感じになります。必要なディスクリプタなどのメモリを設定してTRに読み込めば無事に最初のタスクが走り始めることができる状態になるわけです。タスクの切り替えはタスクゲートと呼ばれるディスクリプタを介して行います。具体的なテスクの切り替え手順はなかなか複雑なので掻い摘んで説明すると、特殊なセレクタの値が入ったセグメント付きJMPまたはCALLを実行すると、CPUが自動的にタスク切換え処理を行い、必要なレジスタを退避してTRを切り替え新しいタスクのレジスタを読み込んで仮想アドレス空間(LDT)を切り替えてくれる訳です。ここにコードは介在しないので、それなりに高速で8MHzの286の場合、22マイクロ秒で切り替わることになっています(一般的な命令は数百ナノ秒で実行できますから大雑把に50命令くらいの時間はかかるわけです)。もちろんタスクがスワップされていて実メモリの内容をディスクから読み込むなんていうことになれば、それこそミリ秒を単位とする時間がかかってしまう訳です(もう3桁ほど時間軸が大きくなる)。

いずれにせよ、一般的なプログラムからはまったく知らぬ存ぜぬの世界で、この手の処理はOSがゴリゴリと処理していたのですが、ハードウェアが面倒を見てくれるようになったのは実に画期的なことでした。これで世界がバラ色になったのかといえば、普通はOSとのデータのやり取りがありますし、ライブラリとして動作するプログラムを複数のプログラムで共有するためにも、いろいろ手順が必要となりました。今までは単にデータへのポインタを渡せば済んだ話が、たとえコードとしてはポインタを渡しているだけでも、それがタスクを跨げばバタバタと内部的なテーブルを切り替えて実行することになるので、少なくとも当時のCPUの能力ではなかなか時間のかかってしまうので、いろいろな工夫を必要なことになりました。結局、何らかの手順でプログラムの間でデータは共有されるには違いがないので、ココに問題があれば、せっかくのメモリ保護も役立たずで、結局、システム全体がダメージを受けてタスクを切り離させば大丈夫とはいかなくなるのが、悲しい現実です。

ということで、80286のプロテクトモードはOS 2などで積極的に活用されたもののWindowsでは原則リアルモードで使われ、せっかくの高度な機能は眠ったままになっていました。さて、あともう少し特権と例外のところまでは説明しておきましょうかね。今回はここまでということで。

ヘッダ画像はAIに描いてもらいました。



この記事が気に入ったらサポートをしてみませんか?