はじめに
コンピュータが複数のタスクを同時に実行できるのは、OSとCPUが連携しながら細かくタスクの切り替えを行なっているからです。今回は、OSの中心部分であるカーネルがCPUの複数タスクの実行を制御する様子、流れを見ていきたいと思います。
前半部分は予備知識のまとめで、後半の「カーネルがCPUの複数タスクの実行を制御する一連の流れ」が本題になります。
カーネル
カーネルとは、オペレーティングシステム (OS) の中核部分であり、ハードウェアとアプリケーションの間で仲介役となるプログラムです。
カーネルの機能
カーネルの基本的な機能を以下にまとめます。
プロセス管理
実行するプロセスの状態を管理し、それぞれのプロセスにCPUの利用時間を割り当てます。カーネル内のプロセススケジューラがそれらの処理を行います。
プロセスの状態には以下の3つがあります。
実行中状態:CPUを割り当てられて実行中の状態
レディ状態:CPUの割り当て待ちの状態
待ち状態:入出力の完了など何かしらの事象を待っており、実行はできない状態
プロセス
プロセスとは、OSの視点から見たアプリケーションのことです。より正確には、アプリケーションとその実行環境(CPUの状態、メモリ空間など)からなる仮想的なコンピュータがプロセスです。
メモリ管理
プロセスが生成されると、そのプロセス用に独立したメモリ領域(アドレス空間)を割り当てます。そして、プロセスが終了すると、そのメモリ領域は解放され、他のプロセスが利用できるようになります。
入出力制御
プロセスと入出力制御装置の間で入出力を制御します。プロセスから入出力要求(システムコール)を受け取ると入出力制御装置に対して入出力命令を出します。
ファイル管理
データを保存するためのファイルシステムを提供し、ファイルの読み書き、作成、削除などの操作を行います。ファイルシステムでは、ファイルやディレクトリは木構造になって管理され、親ディレクトリはその下位のディレクトリやファイルをポインタで指し示します。
カーネルは、このような重要な処理を担当するため、CPUのカーネルモードで実行されます。
CPUの実行モード
CPUは、次の2つの実行モードのいずれかで処理を行います。
ユーザーモード
一般的なアプリケーション(プロセス)は、ユーザーモードで実行され、ハードウェアに直接アクセスすることができません。ハードウェアにアクセスしたい時には、OSが提供するシステムコール関数を呼び出すことで、間接的にハードウェアにアクセスすることができます。
これによりアプリケーションが、システム内のメモリやHDDなどのリソースに直接アクセスできなくなるため、システムの安全性が確保されます。
カーネルモード
CPUがカーネルを実行するためのモードで、メモリやHDDなどのハードウェアに対して制限なしでアクセスできます。ユーザーモードで実行中のプロセスがシステムコールを発行することで、CPUはカーネルモードに切り替わります。
割り込み
割り込みにより、CPUが実行中のプロセスを一時停止させ、別のプロセスを実行することが可能になります。割り込みには、ハードウェア割込みとソフトウェア割込みがあります。
ハードウェア割込み
ハードウェア割込みは、ハードウェアがCPUに対してイベントの発生を通知するための仕組みです。ハードウェア割込みには以下のような種類があります。
入出力割込み:入出力制御装置が入出力動作を完了したことを知らせる
タイマ割込み:所定の時間(タイムスライス)が経過したことを知らせる
ソフトウェア割込み
ソフトウェア割込みは、実行中のプロセスが意図的にCPUに割り込みを行う仕組みです。プロセスは、カーネルの機能を使用してハードウェアにアクセスしたいときに、ソフトウェア割込みを使用します。
カーネル呼び出し割込み:プロセスがカーネルの機能を利用したいときに、システムコールを発行することで発生する割り込み
他にも、アドレス変換例外割込みなどがあります。
カーネルがCPUの複数タスクの実行を制御する一連の流れ
今回は、カーネルがシングルプロセッサ上で動作する3つのプロセスを制御する様子を見ていきます。プロセスAがHDDからファイルデータを読み込む処理を行い、プロセスBとプロセスCはその他の処理を担当するという前提で話を進めていきます。
以下が全体像になります。
1. 実行中のプロセスAがシステムコールを発行
実行中のプロセスAが、ハードディスク(HDD)に保存されているファイルデータを取得するため、システムコール関数read()
を発行し、カーネルに処理を依頼します。
2. カーネル呼出し命令の実行
read()
関数内でカーネル呼出し命令が実行されます。
3. カーネル呼出し割込みの発生
カーネル呼出し命令により、カーネル呼出し割込みが発生します。
4. CPUが割込みを検知
CPUがカーネル呼び出し割り込みを検知すると、実行モードがユーザーモードからカーネルモードに切り替わります。これにより、CPUはカーネル内のプログラムを実行できるようになり、メモリや外部記憶装置など、すべてのハードウェアにアクセスできるようになります。
5. 割込み処理1の実行
割込み処理1の部分がカーネルへの入り口となります。ここで割込みの種類が特定され、要求された処理が実行されます。
要求されたカーネル処理を実行する前に、割り込まれたプロセスAの実行状態をメモリ内の特定の領域に退避させます。これにより後でプロセスAの実行を再開できるようにします。
6. ファイル管理、入出力制御の処理
ファイル管理、入出力制御のプログラムが実行され、その中で入出力制御装置に入出力命令が出されます。
7. 入出力制御装置がHDDの読み込み動作を実行
入出力制御装置は受け取った命令に従って、HDDに対して読み込み操作を行います。
CPUが実際の入出力動作を制御すると、CPUはその間他のタスクを実行できなくなります。それでは効率が悪いため、実際の入出力動作は入出力制御装置に行ってもらいます。
8. カーネル出口処理
カーネル内で必要な処理が完了すると、カーネル出口処理が実行されます。
プロセスAは入力処理の完了を待っており、プロセススケジューラのsleepルーチン
により待ち状態になりました。そこで、プロセススケジューラのカーネル出口ルーチン
が、次に実行するプロセスを選択します。今回の例では、プロセスBかプロセスCのどちらかを選びます。プロセスBが選ばれたとすると、プロセスBの状態がレディから実行中に切り替わります。
そして、プロセスBの処理を再開するために、メモリ内の特定の領域に退避していたプロセスBの以前の実行状態を取り出し、CPUにセットします。これにより、CPUの実行モードがユーザーモードに切り替わります。
9. プロセスBの実行再開
プロセスBの実行が、以前の続きの状態から再開されます。
10. 入出力割り込みの発生
その後、読み込み動作が完了すると、入出力制御装置がCPUに対して入出力割込みを発生させます。CPUが割り込みを検知すると、カーネルモードに切り替わりカーネルが実行されます。このとき、実行中のプロセスBを一時中断し、割り込み処理を優先的に実行します。
11. 割込み処理2の実行
割り込み処理2の部分がカーネルへの入り口となり、入出力完了処理やファイル管理の後処理が行われます。そして、プロセススケジューラはwakeupルーチン
を実行することで、プロセスAの待ち状態を解除し、レディ状態にします。
12. カーネル出口処理
プロセススケジューラの出口処理ルーチンにより、次に実行するプロセスとしてプロセスAが選ばれたとします。そうすると、特定のメモリ領域に退避していたプロセスAの以前の実行状態がCPUにセットされ、CPUは再びユーザーモードに切り替わります。
13. プロセスAの実行の再開
プロセスAの実行が、以前の続きの状態から再開されます。
14. read()の次の処理に進む
システムコール関数read()
の処理が完了すると、read()
の次の処理に進みます。
カーネル処理のまとめ
以下のようにカーネルとCPUが連携しながらプロセスを高速に切り替えることで、ユーザーは同時に複数のアプリケーションを操作することができます。
今回は、シングルプロセッサ上で3つのプロセスを並行して実行する様子を見てきましたが、マルチプロセッサ環境では、プロセッサの数だけプロセスを並列に実行することができます。
参考
IT Text オペレーティングシステム 改訂2版 (野口健一郎・光来健一・品川高廣)
[試して理解]Linuxのしくみ ―実験と図解で学ぶOS、仮想マシン、コンテナの基礎知識 増補改訂版(武内 覚 )