2025/10/17 13:04 Elixir/Ports and external process wiring

ロボ子、Elixirでrsyncライブラリを作ってる人がおるみたいじゃぞ。外部プロセスとの連携で色々苦労してるみたいじゃ。

rsyncですか。ファイル同期に使うあれですね。Elixirから外部プロセスを扱うのは、そんなに大変なんですか?

`System.shell`とか`System.cmd`は、プロセスが終わるまでElixirのスレッドが止まってしまうからの。非同期に実行したいなら`Port.open`を使うのがミソじゃ。

`Port.open`ですか。rsyncの進捗状況はどうやって取得するんですか?

rsyncには`--info=progress2`という便利なオプションがあるんじゃ。これを使うと、進捗状況が`:data`としてコールバックに送られてくる。終わったら`:exit_status`が来るぞ。

なるほど。rsyncの出力行が`:data`として送られてくるんですね。キャリッジリターン`\r`でターミナル上の表示を上書きするんですね。

そうそう。Erlang/OTPの`gen_server`で`Port`呼び出しをラップすると、専用スレッドでrsyncを制御できるんじゃ。便利じゃろ?

確かに便利そうですが、記事には「`gen_server`が停止しても、外部の子プロセスは停止しない問題が発生」とありますね。

`sleep 60`コマンドでも同じ現象が起きるらしいぞ。Elixirが終わってもプロセスが残ってしまうんじゃ。

標準入力が閉じられても、rsyncのようなデーモンプロセスは動き続けるんですね。対策はあるんですか?

stdinのクローズを検知してSIGTERMを送るアダプターとか、`nohup`みたいなツールがあるみたいじゃな。Cプログラムでrsyncをラップして、`port_close`をSIGHUPに変換するshimも作ったらしいぞ。でも、SIGTERMが適切じゃな。

erlexecライブラリを参考に、外部プロセスのクリーンアップ処理を改善するアイデアもあるんですね。

そうなんじゃ。BEAMコミュニティでは、外部プロセスの脆弱なクリーンアップはバグとして認識されてるからの。`port_close`をSIGTERMに適応させるPR#9453がレビュー待ちみたいじゃ。

どのシグナルを使うかはまだ未解決なんですね。HUP, TERM, KILLのどれが良いんでしょうか。

Windowsには`erl_child_setup`レイヤーがないとか、プラットフォームによって実装が違うのも悩ましいところじゃ。

erlexecライブラリから、子プロセスのプロセスグループ全体をkillするオプションを導入するアイデアもあるんですね。それが一番確実かもしれませんね。

ほんとじゃな。しかし、外部プロセスの扱いは奥が深いぞ。まるで、私の研究室の掃除みたいじゃ。いつも終わったと思ったら、どこかにゴミが残ってるんじゃから。

博士、それは掃除の仕方に問題があるのでは…?
⚠️この記事は生成AIによるコンテンツを含み、ハルシネーションの可能性があります。