概要
- pythonを使ってプロセスの生成周りを学習し直したメモ
- subprocessモジュールなどもあるが、自分で実装して勉強し直したメモ
- 備忘録
前提
- 環境
- python: v.3.7.7
基礎
-
os.fork()
- fork により、そのプロセスのコピーができる。
- 複製された方が「子」、作った方は「親」
- 子プロセスでは
0
が返り、親プロセスでは子プロセスのid
が返る - forkしたプロセスは、親プロセスが処理終了を待つことはない
- 親プロセスが先に終了すると、子プロセスで
os._exit()
してもゾンビプロセスとして残ってしまうので注意
- 親プロセスが先に終了すると、子プロセスで
- つまり
- forkすると、その時点で処理が分岐される
- 親は子の終了は待たない
- 分岐した際に、自分が子or親の判定は、fork()の返り値が
0
or!0
で判定する - 定石としては、親プロセス側の処理として子プロセスの処理を待つ(waitpidなど)で待つか放置して待つのか、killするのかを書く
- 放置した場合は、子プロセスが終了してもゾンビとして残る可能性がある
-
os._exit()
- fork()によって作られた子プロセスを終了させる
-
os.exec()
- 外部コマンドを実行する
-
exec
の引数は以下例の組合せで変わる-
v
: tuple もしくは list で実行するプログラムの引数を指定する- 例)
execvp('python', ['python', 'test.py'])
- 例)
-
l
は文字列を指定する- 例)
execlp('python', 'python', 'test.py')
- 例)
-
p
: 実行するプログラムをシステムパス (ie. PATH
) から探す -
e
: 実行時に指定する環境変数の Dictionary を最後の引数で指定できる。指定するとこの関数を呼んだプログラムが持っていた環境変数(=PCにデフォルトで設定されている環境変数)は引き継がれないので、追加する場合はos.environ
で取得した環境変数に追加したものを渡す
-
-
os.waitpid(sid, option)
-
option=0
で指定したプロセスIDの処理が終わるまで待つ- これをかまさないと、子プロセスで
os._exec()
してもプロセスがゾンビとして残り続ける。つまり、子プロセスが終了しようとしたとき、親プロセスがwait
してないと、そのプロセスはゾンビ状態となって残る
- これをかまさないと、子プロセスで
-
option=os.WNOHANG
とすると、子プロセスの状態を判定する。この時、呼び出し元のプロセスは、ブロッキングされない(=待ち合わせない)ため、子プロセスの終了を待ち続けない。 -
os.setid()
- プロセスグループのリーダとなる = 制御端末を持つ
- 制御端末を持つまでの流れ
- 新しいセッションを作成する。
- 呼び出したプロセスを新しいセッションのリーダーにする。
- プロセスを制御端末から切り離す。
-
- memo:
- 子プロセスが先に
_exit
したあと、親がwaitpid
するとどうなるのか?子供は先にkill
されてゾンビにならないのか?- きちんと
waitpid
をすることで子プロセスがkill
された。逆に、子が先に処理が終わったからといってwaitpid
しないとゾンビになるので注意。
- きちんと
- 子プロセスが先に
応用
- djangoにおいて、リクエストを返した後も長時間ジョブとしてプロセスで処理を回したい場合の実装例
- double fork
- 実験
- 1回
fork
してrunscript
-> 親はwait
せずにreturn
→ 当然子プロセスはゾンビ化 - 2回
fork
すると孫プロセスはinit配下の子プロセスとなる → 孫プロセスが終了する → ゾンビ化せずに消える
- 1回
- キモは
setsid()
が効いていることで、孫プロセスがデーモン化した- 子プロセスは
fork
後、セッションリーダーとなり、制御端末を親プロセスとは別にもつ - 子プロセスは孫プロセスを
fork
- 子プロセスは即時
exit()
して親プロセスはwaitpid
で待ち合わせる→子プロセスのkill
とゾンビ化を防ぐ - 孫プロセスはセッションリーダーでないことが自明なのでデーモンとして制御端末を持たないことが約束される
- 孫プロセスは子プロセスが終了するとinitプロセス(root)配下においてプロセスの後処理をしてくれるので、処理終了後ゾンビにならない
- 子プロセスは
- 実験
- double fork
- セッションとプロセスについて
- すべてのプロセスは、セッションに属するグループに属し、階層が存在する。
- セッション(SID)→プロセスグループ(PGID)→プロセス(PID)
- プロセスグループ内の最初のプロセスがプロセスグループリーダーになる
- セッション内の最初のプロセスがセッションリーダーになる
- すべてのセッションに1つのTTYを関連付け、セッションリーダーのみがTTYを制御できる。
- プロセスがデーモン化される(バックグラウンドで実行される)ためには、セッションリーダが
kill
されてセッションがTTYを制御する可能性がないようにする必要がある