「実行中のプログラム」がどんなメモリ構造を持ち、OSがどう生成・終了・切替するかを可視化します。
プロセス(Process)は「実行中のプログラム」のことです。ディスク上にあるただのファイル(例: app.exe)を起動した瞬間、OSはそれをメモリに展開し、独自の状態を持つプロセスを作ります。
プログラムとプロセスの違いは、「料理レシピ」と「実際に作っている料理」に例えられます。レシピ(プログラム)は紙の上で静的ですが、実際に作っている料理(プロセス)は調理中で動的で、独自の状態(材料の量・火加減)を持ちます。同じレシピから複数の料理を同時に作れるのも同じです(同じプログラムから複数プロセスを起動できる)。
上のツールで「プロセスのライフサイクル」を ▶ 自動再生すると、プロセスが生成・実行・終了する一連の流れを動きで確認できます。
プロセスのメモリ空間は4つの領域に分かれています。それぞれの役割を押さえておきましょう。
・コード領域(テキスト):機械語の命令が並ぶ場所。読み取り専用なのでバグで書き換わる事故がない
・データ領域:グローバル変数や静的変数を保管。プログラム開始時にサイズが決まる
・ヒープ領域:実行中に動的に確保するメモリ。malloc() / new で取られる
・スタック領域:関数呼び出しで使う。ローカル変数や戻りアドレスが入る
上のツールのステップ2〜3で、コード・データが先に割り当てられ、続いてヒープとスタックが用意される様子が見られます。ヒープとスタックは互いに向き合うように伸びる(衝突するとスタックオーバーフロー)のがポイントです。
OSは各プロセスについてPCB(プロセス制御ブロック / Process Control Block)という「カルテ」を持っています。プロセスの状態を一元管理するための情報パッケージです。
PCBに含まれる代表的な情報:
・PID:プロセスを識別する一意の番号
・状態:実行中 / 待機 / 準備完了 など
・レジスタ値:プログラムカウンタや汎用レジスタの現在値
・優先度:スケジューリングに使う値
・メモリ管理情報:そのプロセスが使うメモリ範囲
コンテキストスイッチ(後述)のとき、CPUはこのPCBを退避・復元することで「中断したところから再開」できます。PCBはプロセスの記憶と言ってもよいでしょう。
プロセスは互いに完全に独立しています。各プロセスは自分のメモリ空間しか見られないため、1つのプロセスがバグでクラッシュしても他のプロセスにはまったく影響しません。
この独立性は、「集合住宅で隣の部屋」のような関係です。隣の部屋(プロセス2)で火事が起きても、適切な壁(OS のメモリ保護機構)があれば自分の部屋(プロセス1)は無事。アパートの管理人(OS)は壁の管理に責任を持ち、住人同士が勝手に壁を越えられないよう監視します。
上のツールのステップ7で、プロセス2がクラッシュしてもプロセス1が動き続ける様子を確認できます。これがプロセスを使う最大のメリットで、Webブラウザのタブごとに別プロセスにする設計(Chrome 等)もこの独立性を活かしています。
CPUは一度に1つのプロセスしか実行できません。複数プロセスが同時に動いて見えるのは、OSが高速で切り替えているからです。この切替をコンテキストスイッチと呼びます。
コンテキストスイッチで OS が行うこと:
・状態の退避:今動いているプロセスのレジスタ値を PCB に書き込む
・状態の復元:次に動かすプロセスの PCB からレジスタ値を読み出す
・メモリ管理切替:仮想メモリのページテーブルを差し替える
プロセスのコンテキストスイッチは重いのがネックです。メモリ管理情報まで全部入れ替えるため、数マイクロ秒〜数十マイクロ秒のオーバーヘッドが発生します(後述のスレッドはこれが軽い)。
Unix 系 OS(Linux / macOS)でプロセスを増やす基本は fork() システムコールです。親プロセスを丸ごとコピーして子プロセスを作ります。
fork() の特徴:
・親と子は別々のメモリ空間を持つ(コピー直後は内容が同じ)
・fork() の返り値で親子を区別:親には子のPID、子には 0 が返る
・直後に exec() を呼んで別プログラムに置き換える「fork-exec パターン」が一般的
Windows では fork ではなく CreateProcess() という別のAPIを使いますが、「新しいプロセスを作る」という目的は同じです。「fork は親プロセスのコピーを作る」と覚えておくとよいでしょう。
プロセスは常にどれか1つの状態にあり、OSの管理のもとで状態が切り替わります。3つの主な状態を押さえておきましょう。
・実行可能状態(準備完了):CPU を割り当ててもらうのを待っている状態。「待ち行列(=順番待ちの列)」に並んでいるイメージ
・実行中(実行状態):CPU を使って命令を処理している状態。1つの CPU につき同時に1プロセスだけ
・待機中(ブロック状態):ディスク読み込みやキーボード入力など、外部の結果を待っている状態
なぜ3つの状態に分けるのか。それはCPUを無駄なく使うためです。入出力の待ち時間はとても長い(CPU の速度と比べると数十万倍以上)ので、待っているプロセスに CPU を持たせ続けると大きな無駄になります。待機中にCPUを別のプロセスへ回すことで、CPUを休ませずにたくさんの仕事をこなせるようになります。
身近な例では、レジ待ちの行列(実行可能)→ レジ精算中(実行中)→ カードの処理を待つ(待機中)のような流れに似ています。カード処理の間、お客さん(プロセス)はレジ(CPU)を占有せず、別のお客さんが使えるようになると考えるとイメージしやすいです。