見出し画像

タスク管理の進化

タスク管理と言っても、ヒトが仕事をコなすテクニックの話ではなく、コンピュータが複数のプログラムを上手に捌く話です。

タスクという概念は、ひとつのCPUで複数のプログラムを擬似的に同時に動かすための考え方で、CPUリソースが貴重な時代から多くの人が効率よくコンピュータを使うために発展してきました。

Task (computing)

一般的に計算などの処理に比べると、入出力は物理的な装置を相手にするために、CPUから見れば多くの待ち時間が発生します。その間にCPUを待たせずに他の処理をさせたほうがトータルで見れば多くの処理を実行できます。そこで入出力の処理が発生した時点でいったんプログラムを中断して、CPUのレジスタやメモリなどを退避したうえで、次のプログラムの実行を開始します。その後、入出力が完了した時点で、中断していたプログラムの退避していたレジスタやメモリの状態を復元して処理を継続するのです。このそれぞれのプログラムをタスクと呼んで管理するのがモニタプログラムもしくはOSのお仕事です。

マルチタスク

8ビットマイコンの時代でも、このようなタスク処理は主に組み込み用途で使われていました。タスクの切り替えにはハードウェアの機能として提供されている割り込みをキッカケとすることが多く、入出力で接続されているデバイスはCPUとのやりとりに、この割り込み信号を使っていました。このため割り込みに関する機能は、それなりに充実していて同時に割り込みが発生した場合の優先順位を持っていたり、割り込みが発生した場合のレジスタの退避のための特別な命令なども用意されていました。ただこの時代のCPUにはメモリの保護機能もなくすべての情報が共有されるので、他のタスクに影響を与えないように互いにルールを守ってメモリや割り込みの処理を書く必要がありました。

わざわざ他のタスクに悪さをするようなコードを書く人もいませんでしたが、プログラムの不具合で想定していないメモリの値を変更してしまったり、しかるべき手順を踏まずにデバイスの状態を変えてしまったりすれば、もう全体をリセットしなければシステムとしては動作を続けられません。そういった意味ではこの時代のシステムは脆弱で、システムを安定して動作させるためには、全体をしっかりとテストしておくくらいしかありませんでした。もっとも今に比べれば全体のサイズが充分に小さかったので、テストさえサボらなければ大丈夫ではあったのですが。

さて、時代も16ビットとなりシステムも大きくなってくると、すべてのコードが安心というわけにはいかなくなってきます。そこで仮想アドレスの導入に合わせてCPUの機能としてタスクをサポートするようになります。タスクはコードを実行する単位で、プログラムから見たひとつの世界です。ひと塊のコードはタスクの単位で作成され、この単位で実行されたり停止されたりします。

タスクごとに仮想アドレスのメモリ空間を確保され、他のタスクのメモリは敢えて共有させない限り見えることはありません。ですから他のタスクが使っているメモリをうっかり書き換えてしまう心配も無いわけです。またモニタまたはOSが管理しているメモリも他のタスクから見えないので、ここが壊れてシステムが不安定になることも防げますし、タスクの単位でプログラムを停めることもできるので、たとえ暴走してしまったプログラムがあっても、そこだけを切り離すことも可能になるわけです。

こうなると、今度は今まで自由にできたプログラムの間でのメモリのやりとりが出来なくなります。もちろんディスクを介すなどすれば出来るのですが、すぐ隣のメモリを使うためにそんな手間をかけるのは馬鹿馬鹿しいです。またOSが持っている機能を呼び出すことも難しくなってしまいます。OSの機能を呼び出すには8ビットな時代から割り込み命令の形を採ることも多かったのですが、これをAPIとして整理し決められた手順で呼び出すことで、呼び出し側のデータを異なるメモリ空間にあるOSに引き渡す方法が一般的となりました。

さてタスクの間でメモリをやり取りする方法が出来たとしても、これが自由に出来てしまえば、元の混沌とした世界に逆戻りです。そこでタスクには権限という上下関係を作ります。普通のプログラムは低い権限でモニタやOSなどは高い権限にするのです。高い権限のタスクは低い権限のメモリなどを自由に使えるのですが、低い権限のタスクは高い権限のタスクに「お願い」をしてメモリなどを使わせてもらうという形にするのです。

こうして私達が書くような一般のプログラムは、OSに面倒を見てもらって、他のプログラムにチョッカイを出されること無く安心して動かすことが出来ているのです。

プログラムからはAPIを呼び出したあとにOSがどうやって処理をしているかを知ることもなく、どんな実アドレスにメモリを割り当てられているのか(もしくは割り当てられていないのか)も知ることはありません。もちろんOS自身を書くような人にとっては、そんな面倒をすべて見なければならないですし、デバイスドライバに手を出すと、ところどころOSのお仕事が顔を出すこともあります。まあ面倒な仕事は任せるに限るのですが、デバッガなどでライブラリの動作を追っている時に、API呼び出しから先は(普通は)追うことが出来ず、そこで何が起こっているのかメモリにどんな値が置かれているのかを見ることは出来なくなりました(方法はあるんですけどね)。想定された結果が得られない時に、何がどう解釈された結果、想定どおりでないのかの現場を押さえられないモドカシさもありますよね。

仮想アドレスや権限の管理がサポートされるようになり、プログラムが安定して動作するための道具立ては揃ったのですが、OSがその機能を充分に活用しなければやぶ蛇になりがちです。そして、これらの隙を縫うテクニック(正しい手順)に頭を悩ます時代になりました。

PS

タスクとプロセスって似ているけど少し意味が異なります。スレッドもまた別の話。

ヘッダ画像は、以下のものを使わせていただきました。
https://www.irasutoya.com/2012/07/blog-post_26.html

#CPU #OS #タスク #タスク管理 #マルチタスク #権限 #API  

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