はじめに
Linuxでは、Webサーバやデータベース、SSHなど、さまざまなプログラムが同時に動作しています。これらの実行中のプログラムは、Linuxカーネルによって「プロセス」として管理されています。しかし、プロセスが存在しているからといって、常にCPUを使用しているわけではありません。
例えば、ApacheのプロセスがHTTPリクエストを待っている間や、PostgreSQLのプロセスがディスクI/Oの完了を待っている間は、CPUで命令を実行する必要がありません。一方、処理するデータが準備できるとプロセスは実行可能な状態となり、CPUで実行される機会を待ちます。
Linuxでは多数のプロセスやスレッドが動作しているため、実行可能になったすべての処理を同時にCPUで実行できるとは限りません。Linuxカーネルは実行可能なタスクを管理し、その中からCPUで実行する対象を選択します。
つまり、プロセスがCPUを利用する仕組みを理解するには、「プロセスがどのような状態を持つのか」「いつCPUを必要とするのか」「CPUで実行されるまでどのように待つのか」を理解することが重要です。
本記事では、Linuxにおけるプロセスとスレッドの基本から、プロセスの状態、実行可能になったタスクがCPUで実行されるまでの流れについて、分かりやすく解説します。
プロセスとは何か
プロセスとは、Linux上で実行されているプログラムを管理するための単位です。
例えば、lsコマンドを実行すると、Linuxカーネルはlsを実行するためのプロセスを作成します。ApacheやPostgreSQLなどのサービスも、Linux上ではプロセスとして動作しています。
単にストレージ上に保存されているプログラムと、実行中のプロセスは異なります。
プログラムは、ストレージ上に保存されている実行ファイルです。一方、プロセスは、そのプログラムを実際に実行するために必要な情報を持った実行中の存在です。
例えば、lsの実行ファイルがストレージ上に存在しているだけでは、CPUはlsの命令を実行しません。ユーザーがlsを実行するとプロセスが準備され、そのプロセスがCPUへ割り当てられることで命令が実行されます。
プロセスが持つ情報
Linuxカーネルは、それぞれのプロセスを管理するためにさまざまな情報を保持しています。
代表的なものには、次のような情報があります。
- PID(プロセスID)
- 実行状態
- 仮想アドレス空間
- 開いているファイルに関する情報
- ユーザーやグループなどの情報
PIDは、それぞれのプロセスを識別するためにLinuxカーネルが割り当てる番号です。例えば、psコマンドを実行すると、現在動作しているプロセスとPIDを確認できます。
また、プロセスごとに独立した仮想アドレス空間が与えられ、プログラムのコードやデータ、Heap、Stackなどが配置されます。
CPUとの関係で特に重要なのが、プロセスの実行状態やCPUレジスタの状態です。
Linuxでは多数のプロセスが存在していますが、それらすべてが常にCPU上で実行されているわけではありません。Linuxカーネルは、それぞれのプロセスが実行可能なのか、何らかの処理を待っているのかといった状態を管理しています。
Linuxカーネルがプロセスを管理する
Linuxカーネルは、プロセスごとの情報を管理しながら、どのプロセスをCPUで実行するのかを制御しています。
あるプロセスがCPUで実行されている途中で別のプロセスへ切り替わる場合、後から処理を再開できるようにCPUレジスタなどの実行状態を保存しておく必要があります。
そして、そのプロセスが再びCPUへ割り当てられると、保存していた状態を復元して処理を再開します。
このように、Linuxにおけるプロセスは単なる「実行中のプログラム」ではなく、プログラムを実行するために必要なさまざまな情報をLinuxカーネルが管理するための単位です。
ただし、実際にCPUへ割り当てられる単位を理解するには、プロセスだけでなく「スレッド」についても理解する必要があります。次に、プロセスとスレッドの関係について見ていきます。
プロセスとスレッドの関係
Linuxでは、プログラムを実行するとプロセスとして管理されます。一方、プロセスの中では、実際の処理を行う単位として「スレッド」が動作します。
1つのプロセスは少なくとも1つのスレッドを持ち、必要に応じて複数のスレッドを作成できます。
例えば、あるWebサーバのプロセスが複数のスレッドを持ち、それぞれのスレッドで異なるリクエストを処理するといった使い方があります。

プロセスとスレッドの違い
プロセスとスレッドの大きな違いは、プロセス内の資源を共有するかどうかです。
プロセスは、それぞれ独立した仮想アドレス空間を持っています。
一方、同じプロセスに属する複数のスレッドは、同じ仮想アドレス空間や開いているファイルなど、多くの資源を共有します。ただし、すべての情報を共有しているわけではありません。各スレッドは、それぞれ独立して命令を実行するために、CPUレジスタの状態やスタックなどを個別に持っています。
整理すると、次のようになります。
| 比較項目 | プロセス | スレッド |
|---|---|---|
| 仮想アドレス空間 | プロセスごとに独立 | 同じプロセス内で共有 |
| コード・データ・ヒープ | プロセスごとに持つ | 同じプロセス内で共有 |
| スタック | − | スレッドごとに持つ |
| レジスタ状態 | − | スレッドごとに持つ |
| 資源の共有 | 他プロセスとは基本的に分離 | 同じプロセス内で多くの資源を共有 |
CPUで実行されるのはスレッド
CPUとの関係を理解するうえで重要なのは、Linuxでは実際の実行単位がスレッドであるという点です。
例えば、1つのプロセスに4つのスレッドが存在する場合、それぞれのスレッドが独立してCPUで実行される対象になります。
複数のCPUコアが利用できる環境では、異なるスレッドが別々のCPU上で同時に実行されることもあります。
そのため、「プロセスにCPUが割り当てられる」と表現することもありますが、より正確には、そのプロセスに属するスレッドがCPUで実行されます。
Linuxではプロセスとスレッドをタスクとして扱う
Linuxカーネルでは、プロセスとスレッドを完全に別の仕組みとして管理しているわけではありません。
Linuxでは、CPUで実行されるプロセスやスレッドを「タスク」として管理しています。カーネル内部では、それぞれのタスクに対して実行状態やスケジューリングに必要な情報が保持されています。
CPUスケジューラは、実行可能なタスクの中から次にCPUで実行する対象を選択します。つまり、CPUとの関係を単純化すると、次のように考えることができます。
プロセス
↓
1つ以上のスレッド
↓
Linuxカーネルがタスクとして管理
↓
CPUスケジューラが実行対象を選択
↓
CPUで命令を実行
以下に、イメージ図を記載します。

この関係を理解しておくと、「なぜ1つのプロセスが複数のCPUを利用できるのか」「LinuxのCPUスケジューラは何を切り替えているのか」といった仕組みも理解しやすくなります。
次に、これらのタスクが常にCPU上で実行されているわけではないことを理解するために、Linuxにおけるプロセスの状態について見ていきます。
セマフォ
複数のプロセスやスレッドが同じリソースへ同時にアクセスすると、処理が競合して予期しない結果になることがあります。このような競合を防ぐために利用される仕組みの一つがセマフォ(Semaphore)です。
セマフォは、共有リソースへ同時にアクセスできるプロセスやスレッドの数を制御します。例えば、セマフォの値が1の場合、同時にアクセスできるのは1つだけです。

セマフォを取得できないプロセスは待機状態となり、CPUはその間に別のタスクを実行できます。
プロセスの状態と遷移
Linux上のプロセスは、生成されてから終了するまで、常にCPUで実行されているわけではありません。
CPUで実行されている状態、CPUが割り当てられるのを待っている状態、I/Oやイベントの完了を待っている状態などを行き来しながら処理を進めます。

主な状態と遷移は、上図のようになります。
実行可能状態(Runnable)
プロセスが生成され、CPUで実行できる状態になると、実行可能状態になります。実行可能状態のプロセスは、すぐにCPUで実行されるとは限りません。CPUスケジューラによって選択され、CPUが割り当てられるのを待ちます。CPUスケジューラによって選択されると、CPU実行権を得て実行状態へ移ります。
実行状態(Running)
実行状態は、実際にCPU上で命令を実行している状態です。CPU実行権を失うと、再び実行可能状態へ戻り、次にCPUが割り当てられるのを待ちます。また、実行中にI/Oやイベントの待ちが発生した場合は、CPUを手放してスリープ状態へ移ります。
※Linuxカーネルでは、実行可能状態とCPUで実行状態は、どちらもTASK_RUNNINGとして扱われます。本記事ではCPUとの関係を分かりやすくするため、RunnableとRunningを分けて説明しています。
スリープ状態(Sleeping)
スリープ状態は、I/Oやイベントなどの完了を待っている状態です。例えば、ストレージからのデータ読み込みなどで待ちが発生すると、CPUを使用する必要がなくなるためスリープ状態になります。待っていたI/Oやイベントが完了すると、実行可能状態へ戻り、再びCPUが割り当てられるのを待ちます。
ゾンビ状態(Zombie)
プロセスの処理が完了すると実行を終了します。ただし、親プロセスが終了ステータスなどの情報をまだ回収していない場合、そのプロセスはゾンビ状態として残ります。ゾンビ状態ではすでにプログラムの実行は終了しているため、CPUで命令を実行することはありません。
プロセスがCPUで実行されるまで
実行可能状態(Runnable)となったタスクは、どのようにCPUで実行されるのでしょうか。
Linuxでは、CPUスケジューラが実行可能なタスクの中から、次にCPUで実行するタスクを選択します。大まかな流れは次のとおりです。

選択されたタスクはCPU上で命令を実行します。そして、I/O待ちが発生したり、別のタスクへCPUを割り当てる必要が生じたりすると、CPUでの実行を終了または中断します。
このようにLinuxでは、多数の実行可能なタスクの中からCPUスケジューラが実行対象を選択することで、限られたCPUを複数のプロセスやスレッドで共有しています。
まとめ
本記事では、Linuxにおけるプロセスとスレッドの関係から、プロセスの状態、CPUで実行されるまでの流れについて解説しました。
Linuxでは、実行中のプログラムはプロセスとして管理され、プロセスの中では1つ以上のスレッドが動作します。同じプロセスに属するスレッドは仮想アドレス空間などを共有しながら、それぞれがCPUで実行される対象となります。
Linuxカーネルは、これらをタスクとして管理しています。タスクは常にCPUを使用しているわけではなく、Runnable、Running、Sleepingなどの状態を遷移しながら処理を進めます。
実行可能状態(Runnable)となったタスクは、CPUスケジューラによって選択されるとCPU上で命令を実行します。
LinuxにおけるCPUの仕組みについては、以下の書籍にまとめています。興味がありましたら、ぜひ読んでみてください。
LinuxのCPUの仕組みを
図解で理解したい方へ
命令実行・プロセス・スケジューラ・割り込み・ キャッシュ・マルチコアなど、 LinuxにおけるCPUの仕組みを図解を中心にわかりやすく解説しています。
Kindleで書籍を見る →


コメント