🗂

Pythonで実際に動かしてプロセスを理解する

2025/03/23に公開

概要

「プロセスって聞いたことあるけど、なんとなくしかわからない…」状態だったため、超初歩的な内容ながらも、実際にPythonを使ってプロセスの基礎の基礎を見ていきます。

プロセスとは

プロセスは、プログラムが実行され、メモリが割り当てられて実際にCPU上で動作している状態のことを指します。

プログラムとの違い

  • プログラム: ハードディスクなどに保存されている、実行可能なコード
  • プロセス: プログラムを実行している最中の状態のこと。メモリを割り当てられ、CPU上で動いているもの。

例えるならば、プログラムはレシピ、プロセスはそのレシピをもとに実際に料理している行為そのものを指すイメージでしょうか。

プロセスを見てみる

以下のPythonスクリプトは、実行中のプロセスが繰り返し出力を行う様子を観察するためのものです。

test.py
import os
import time

print(f"プロセス開始: PID={os.getpid()}")
for i in range(100):
    print(f"[{i+1}] Running... PID={os.getpid()}")
    time.sleep(2)

実行結果は次のとおりです。

ProcessTest % python3 test.py
プロセス開始: PID=87364
[1] Running... PID=87364
[2] Running... PID=87364
[3] Running... PID=87364
....

このプロセスが実行中の間に、別のターミナルを開き、プロセスを確認してみます。

すると、実行中のプロセスの存在が確認できます。

% ps -a | grep python
87369 ttys003    0:00.00 grep python
87364 ttys008    0:00.03 /opt/homebrew/Cellar/python@3.12/3.12.5/Frameworks/Python.framework/Versions/3.12/Resources/Python.app/Contents/MacOS/Python process.py

当たり前ですが、この処理が終了すればプロセスは無くなります。

% ps -a | grep python
87836 ttys003    0:00.00 grep python

子プロセス

プロセスは、新しいプロセス(=子プロセス)を作成することができます。

以下は、os.fork()を使ってプロセスを複製し、親プロセスと子プロセスがそれぞれ別の処理を実行するサンプルです。

test.py
import os
import time

def main():
    pid = os.fork()

    if pid > 0:
        print(f"[親] PID: {os.getpid()}, 子PID: {pid}")
        print("[親]を終了します。")
    else:
        print(f"[子] PID: {os.getpid()}, 親PID: {os.getppid()}")
        for i in range(3):
            print(f"[子] {i+1} 秒目の処理中")
            time.sleep(1)
        print("子プロセスを終了します。")

if __name__ == "__main__":
    main()

結果は次のとおりです。

ProcessTest % python3 test.py
[] PID: 82114, 子PID: 82115
[]を終了します。
[] PID: 82115, 親PID: 82114
[] 1 秒目の処理中
# 親プロセスが終わりプロンプトが戻るが、子プロセスは処理を継続する
ProcessTest % [] 2 秒目の処理中
[] 3 秒目の処理中
子プロセスを終了します。

この出力結果から、親プロセスfork()によって複製された子プロセスがそれぞれ独立して存在していることが読み取れます。親プロセスの処理が終了したタイミングでプロンプトが戻っていますが、その後も子プロセスは処理を継続しています。このことからも親子のプロセスが独立かつ並行で処理されていることがよく分かります。

このソースコードの流れを整理してみます。

  • python3 test.py を実行すると、1つのプロセス(親)が起動します
  • os.fork() が呼ばれると、その親プロセスがもう1つの自分にそっくりなプロセス(子)を複製します
  • 親と子はどちらもos.fork()の次の行から同じコードを実行します
  • os.fork()の戻り値で 親 か 子 かのどちらかを判別できます
    • 親プロセスのos.fork()の戻り値: 子プロセスのPID
    • 子プロセスのos.fork()の戻り値: 0
  • その後、親と子はそれぞれの処理を進めます

Discussion