Linuxのカーネルの開発に関する世界観(プロセス管理)です。
OSでプログラムを同時に並列実行するためには、「プロセスの数だけ、専用のCPUとメモリが用意されているかのように見せる」ことが必要。
このために、メモリでは仮想アドレス空間を用いて、論理アドレスを物理アドレスに翻訳し、異なるアドレス空間を同時に持てるようにする。
また、CPUではレジスタの内容を退避し、小さな時間で次々に停止と再実行を切り替えることで、プログラムそれぞれに「タイムスライス」と呼ばれる時間ごとのスケジューリングを行う。
(詳しくはふつうのLinuxプログラミング Linuxの仕組みから学べるgccプログラミングの王道が参考になります。)
自分の書いたブログ「わたしの名はフレイ」2020/06/26より。
まず、LinuxカーネルはマルチタスクなOSのカーネルである。
マルチタスクとは、「複数のプログラムを同時に並列実行できる」ということ。
ここで、プログラムにはプロセスとスレッドがあることが重要である。
プロセスは、それぞれに個別の独立したメモリ領域が与えられる。
それに対して、スレッドはプロセスの中に存在する並列処理であり、同じメモリ領域を参照することができる。
このため、マルチスレッド環境では、排他制御を行う「ロック」が肝心となる。
ロックを適切に行うことで、スレッドセーフな関数を作らなければならない。
プロセスにおいても、プロセス間通信(IPC)と呼ばれる機構を使うことで、メモリを共有したり、簡単な排他制御を行う(セマフォ)ことが可能となっている。
自分の書いたブログ「わたしの名はフレイ」2020/06/26より。
プログラムがもしあったとして、それをマルチタスクで実行するためにどうするか。
プログラムは、実際、機械語でCPU命令とメモリアドレスが記述されているため、単純に考えれば、CPUとメモリがそれぞれのプログラムに必要となる。
しかしながら、マルチタスクのOSでは、プログラムの命令を、とても小さな時間で切り替えながら実行することで、CPUの仮想化を実現する。
つまり、プログラムの実行しているレジスタの内容や、プログラムカウンタなどを、task_structと呼ばれる構造体に退避させて、プログラムを「安全に停止させる」ことができればいい。
この上で、プログラムを次々に停止・再実行する。
これを「コンテキスト切り替え」と呼ぶ。
後日注記:スケジューラによるスケジューリングは、それぞれのプロセスに与えられる個別の時間である「タイムスライス」の配分と管理を行う。
自分の書いたブログ「わたしの名はフレイ」2020/06/26より。
カーネルはプロセスを管理するために、「プロセスディスクリプタ」と呼ばれるプロセスの情報によってプロセスを管理する。
カーネルがプロセスの実行を停止すると、CPUレジスタ内のさまざまな情報がプロセスディスクリプタの中に退避される。
この情報には、プログラムカウンタや汎用レジスタ、浮動小数点レジスタ、プロセッサ制御レジスタ、メモリ管理レジスタなどが含まれる。
カーネルがプログラムの再開を決めると、プロセスディスクリプタに退避された情報を使って、停止されたCPUレジスタを復旧する。
(詳しくは詳解 Linuxカーネル 第2版が参考になります。)
(以下は「オペレーティングシステム―設計と理論およびMINIXによる実装」を参考に執筆しました。)
プロセスには、3つの状態がある。「実行中」「実行可能」「ブロック(待ち状態)」である。
プロセスが実行と停止を繰り返す中で、実行中と実行可能の2つの状態があることは分かる。では、ブロック(待ち状態とも呼ばれる)とは何だろうか。
ブロックとは、たとえば、「プログラムが実行可能ではあるものの、別のプロセスの終了を待機しなければならない」といった状況である。
たとえば、パイプで、あるコマンドの実行(たとえばfind)を別のコマンド(たとえばgrep)が待機している時、grepはfindの実行が終わるまでブロックされる。
このプロセスの3つの状態は、推移する。ある状態は常に別の状態に変わる可能性がある。
また、プロセステーブルには、プロセスの実行の際に必要となるデータが格納されている。プロセスを安全に停止・再実行ができるのは、プロセステーブルにプロセスの状態が保管・維持されているからである。
後日注記:ブロック(待ち状態)には、ほかにもI/Oデバイスから転送されるデータなどを待っている状態などについても言える。問題が解決し次第、実行状態に戻る。(詳しくは放送大学「コンピュータの動作と管理 ('17)」が参考になる。)
2023.05.15編集
UNIXでは、プロセスという「実行コンテキスト」を持っている。
プロセスはプログラムの意味でもあるが、複数のプログラムを連続して順次的に実行するプロセスや、同じプログラムを実行する複数のプロセスというのも考えられる。
昔のOSでは、プロセスはひとつしか存在せず、CPUはプロセスが専有し、メモリのアドレス空間はひとつだけだった。
UNIXはマルチタスクと呼ばれる仕組みを採用しており、タイムシェアリングシステムとも呼ばれる。
UNIXでは、プロセスは複数同時に実行でき、それぞれのプロセスが別々のアドレス空間を持つ。
そのために、プログラムをそれぞれ別々に実行するスケジューラと呼ばれる機構があり、複数のプログラムを時間ベースで次々に切り替えて実行する。
実際は、プログラムを実行している時に別のプログラムの実行が要求されると、プログラムはレジスタなどの必要なデータをレジスタ外に退避させ、そのプログラムを停止する。そして新しいプロセスが実行される。スケジューラによって次々にこの切り替え命令が実行される。切り替えが行われると、今動いているプロセスを停止して別のプロセスを実行できる。
また、プロセスの持つアドレス空間は、実際にアクセスされる前にカーネルによって翻訳される。よって、プログラムがメモリを使用するために必要なアドレス空間を、それぞれのプログラムの中に「閉じる」ことができる。
後日注記:現代的なUNIXカーネルでは、カーネルの中の設計はカーネルスレッドの集合体となることがある。このような場合、通常のプロセスの切り替えよりも、カーネルスレッドの切り替えは効率的に行われる。ただし、Linuxでは限定的な手法でしかカーネルスレッドを利用していない。
(「詳解 Linuxカーネル 第2版」を読みながら自分で考えて書きました。)
2023.05.12編集
(以下は「詳解 Linuxカーネル 第2版」を参考に執筆しました。)
プロセスを再開するに当たって、休止した状態のCPUのレジスタを復元する必要がある。このデータのことをハードウェアコンテキストと呼ぶ。Linuxカーネルにおいては、プロセスディスクリプタとカーネルモードスタックに、このデータが保管される。
プロセスは、schedule()関数によってスケジューリングされる。ここでは二つの切り替えが行われる。
切り替え対象 | 説明 |
---|---|
ページグローバルディレクトリ | ページアドレッシングの中でもっとも最上部にあるディレクトリのこと。 ページグローバルディレクトリを新しいメモリアドレス空間へと変更する。 |
ハードウェアコンテキスト | ハードウェアコンテキストは、レジスタの中の状態のこと。 カーネルの中に格納されたカーネルモードスタックをレジスタに復元する。 |
ハードウェアコンテキストの切り替え処理は、switch_to()マクロが実行する。
switch_to(prev, next, last);
元あったプロセスへの参照を失わないためにprev、next、lastの3つの引数を取る(2つではない)ことに注意。
スケジューリングが行われている実際のコードは以下から参照できます。
2023.04.22編集
タイマは、特定の時間に特定の処理を行う機能。
カーネル内部で使用されるダイナミックタイマと、プロセスで使用されるインターバルタイマがある。
タイマはさまざまなところで使われている「縁の下の力持ち」的な機能である。
詳しくは以下の書籍が参考になります。
2023.02.13編集
以下の書籍が参考になります。
また、以下のページに参考になる内容があります。日本語版もあります。
スケジューリングとは、タイムスライスをそれぞれのプロセスに公平かつ効率的に与えるための管理機構のこと。
スケジューリングのアルゴリズムについては以下を参照のこと。
2023.05.18編集
ラウンドロビンは、実行可能なプロセスをひとつひとつ順番に交代して実行するアルゴリズム。
多段フィードバックキューは、複数のFIFOキューを持ち、上位キューから最初に発見した実行可能なプロセスを実行するアルゴリズム。
多くのOSでプリエンプション(一時的なタスクの停止)ありの多段フィードバックキューが使われている。昔は協調的スケジューラが使われていたOSが多かった。
Linux 2.4では多段フィードバックキューのO(N)スケジューラ、Linux 2.6からはO(1)スケジューラを採用していた。(オーダー記法については具体的なアルゴリズムを参照のこと。)
また、Linux 2.6.23以降では均等化キューイングベース(汎用OSでは初)のCompletely Fair Scheduler(CFS)が使われている。
プロセスやカーネルの情報を得ることのできる特殊なディレクトリ。以下の書籍が参考になります。
UNIXシステム管理(システム情報と設定)も参照のこと。
MINIX本では、プロセス管理・スケジューリングについて詳細が記述されています。
以下のサイトに、Linuxカーネル2.6解読室という書籍の第2章までが公開されています。
CPUについてはCPUやIntelやCPUアーキテクチャやアセンブリ言語を参照のこと。
仮想メモリについてはLinuxカーネル(メモリ管理)を参照のこと。
マルチタスクについてはLinux API(2.プロセス・メモリ)や並列処理を参照のこと。