TL;DR

  • OS がタスクを切り替えるたびにコンテキストスイッチのオーバーヘッドが発生する。
  • プロセスは独立したメモリ空間を持つので互いに影響しにくいが、メモリを多く消費し、起動にも時間がかかる。
  • Go の並行プログラミングでは、重いプロセスではなく軽量なスレッドやゴルーチンを使う。

内容

OS におけるマルチプロセッシング

OS がタスク管理でコンテキストスイッチを行うたびに、オーバーヘッドが発生する。OS は、後で中断したところから再開できるように、現在のプロセスの状態を保存する必要がある。さらに、次に実行するプロセスの状態を読み込む必要もある。この状態はプロセスコンテキストブロック(PCB)に保持される。

タスク管理をはじめとする OS の仕事は、シングルコアでもマルチコアでも大きくは変わらない。違うのは、スケジューラが複数の CPU にプロセスを割り当てる必要がある点である。

プロセスによる並行処理

OS の各プロセスは、他のプロセスから独立したメモリ空間を持つ。そのため、あるプロセスがクラッシュしても、他のプロセスには影響しない。その代わり、多くのメモリを消費し、起動にも時間がかかる。

プロセスの生成

OS は、プロセスを生成・開始・終了するシステムコールを提供している。

Windows の CreateProcess を呼ぶと、プロセスの作成、資源の割り当て、コードの読み込みを経て実行が始まる。

UNIX の fork を呼ぶと、OS はレジスタ、スタック、ファイルハンドラ、プログラムカウンタを含むメモリ空間と資源の完全なコピーを作る。新しいプロセス(子プロセス)は、その時点から処理を続ける。このコピーはコピーオンライトで最適化されている。fork は、呼び出した親プロセスには子プロセスの PID を返し、子プロセスには 0 を返す。各プロセスは、この戻り値をもとに実行する命令を決める。子プロセスはコピーされた資源をそのまま使うことも、更新したり、消して新しく始めたりすることもできる。プロセスは独立しているので、親プロセスには影響しない。

各プロセスは独立した空間を持つので、プロセスを生成するたびにメモリを消費する。資源のコピーや割り当てにも時間がかかり、CPU サイクルを消費する。そのため、プロセスを多く生成するとシステムに大きな負荷がかかる。

Go でのプロセス操作

Go でのプロセス操作は、OS に依存する syscall パッケージに限られている。UNIX では ForkExec() や StartProcess() でプロセスを作れる。また、Exec() を使うと、新しいプロセスでコマンドを実行できる。ただし、Go の並行プログラミングでは重いプロセスは使わず、軽量なスレッドやゴルーチンを使う。

キーワード

自分の理解

Windows 向けに CreateProcess() に当たる関数がないかと Go の syscall パッケージを調べたが、見つからなかった(Deprecated にもなかった)。