まえがき
- std::processはポータブルな実装だがにここではunix着目する
プロセス起動
- 新しいプロセスを立ち上げてプログラムを実行する
- サブプロセスと言ったりもする
プロセス
- プログラムを実行している主体
- いろいろ状態をもってる
- メモリ空間
- ファイルディスクリプタ
- 環境変数
prcess::Command
- プロセス起動を行うためのAPI
- 実装はポータブル(Posix/Windows)
- UNIXでいうfork-execを行う
- シェルを経由する専用APIは用意しない
- newやargの引数に
sh -c
を渡せばできる
- newやargの引数に
イメージ
- 以下の例ではシェルを経由して実行
-
output
で出力を受け取るまで暗黙にwait
-
let output = Command::new("sh")
.arg("-c")
.arg("echo hello")
.output()
.expect("failed to execute process");
デザイン
-
Command::new
時点ではプロセスを生成しない-
spawn
などを呼ぶまでの間にやりたいことを設定- 引数の追加(
arg/args
) - リダイレクト(
stdin/stdout/stderr
) - 環境変数の追加/クリア(
env_clear
) - カレントディレクトリ(
current_dir
)
- 引数の追加(
-
デザイン
- waitするかどうかはAPIによって変わる
- spawnはwaitしないので親が明示的に行う
- outputやstatusは当然行う
spawn
- ジェネリックなプロセス起動API
-
std::process::Child
を返す- 子プロセスのPIDをもつ
- waitをメソッドにもつ
spawnの内部実装
- 条件が合えば
posix_spawn
を使う - そうでない場合
fork
とexec
を使う
posix_spawn
- fork-execまで一気にやってくれるAPI
- 現時点でいくつかのプラットフォームで使える(ただしそれぞれいくつか制約条件はある)
- Linux/Glibc2.24+, MacOS , FreeBSD
posix_spawnを使う意図
- メモリを複製しない実装が使われることが多い
- Linuxなら
注:コメント欄で指摘いただきましたclone(CLONE_VM/E_VFORK)
- Linux: clone(CLONE_VM|CLONE_VFORK)
- MacOS/FreeBSDなら
vfork
- Linuxなら
- fork-exec間に差し込めない処理もある
-
current_dir
など使ったらforkを使う
-
before_exec
- UNIXなシステムのみ存在
- Windowsではそもそも不可能
- fork-exec間で行いたい処理をフック可能
- 他ではPythonの
preexec_fn
などがある
- 他ではPythonの
- リダイレクトや環境変数以外にもやりたいことはある
- リソース管理 (例: core dump)
- fdのclose処理を書きたいかも
before_execは問題ないのか
- 任意の処理が記述できてしまう
- fork-exec間はasync-signal-safeな関数のみ使うべき
- 危険性はドキュメントで注意喚起している
- Pythonも同様
FDの挙動
- 他の言語のように3番以降のFDをcloseするAPIを提供していない
- Rustのstd自体はCLOEXEC前提
- execのときにcloseするフラグ
- Cのライブラリ関数は保証がない
- リークしちゃうかも
- Rustのstd自体はCLOEXEC前提
過去の議論
-
https://github.com/rust-lang/rust/pull/27609
- 標準ライブラリの範囲では不要という判断
- どうしても必要なら
before_exec
でできる - 将来的には変わるかも
spawnのまとめ
- プロセス起動のプリミティブなAPI
- fork+属性設定+exec
- posix_spawnを使えるときは使う
-
before_exec
で振る舞いを自由に記述できる- 注意は必要