notes
Ros

短命プロセスの後始末は cgroup に任せる (systemd-run --scope)

ros2 run/launch の子は親の terminate で死なず残留・混信する。setsid+killpg の手書きは手続き依存で失敗する。systemd-run --user --scope + RuntimeMaxSec なら掃除を忘れても cgroup ごと必ず死ぬ

要点: 短命の検証プロセス (ROS プローブ等) は systemd-run --user --scope --collect -p RuntimeMaxSec=N -- <cmd> で起動する。プロセスツリーが cgroup で丸ごと管理され、掃除の手続きを忘れても時間で必ず全滅する。

問題#

  • ros2 run / ros2 launch はラッパーで、親への SIGTERM が実行体まで届かないことがある
  • 生き残りノードは DDS が自動発見するので、次の検証と混信する (「設定を変えたのに挙動が変わらない」の典型原因)
  • launch は悪質: セッションリーダーだけ死んで子ノード多数が孤児化した実例あり (残った teleop が数時間後に機体を動かした)

従来の対処と欠点#

手法欠点
proc.terminate()子が残る
setsid + killpgkillpg を忘れる・書き間違えると孤児。毎回の手書きが必要
pkill -f <pattern>パターンが自分のシェルのコマンド文字列に一致して自爆 (exit 144)
timeout N setsid ...SIGTERM がセッションリーダーにしか届かないケースあり

共通の弱点: 手続きに依存していて、1 回忘れると事故る。

解決: systemd-run --user --scope#

systemd-run --user --scope --collect --quiet -p RuntimeMaxSec=5 -- \
  ros2 topic echo /joint_states --once
  • scope = cgroup。fork した孫プロセスも全部同じ cgroup に属す
  • RuntimeMaxSec 経過で cgroup ごと SIGTERM → SIGKILL。構造で殺すので取り漏らしが原理的に無い
  • --collect で失敗 unit も残らない
  • フォアグラウンドでブロックするので、呼び出し側の timeout / Ctrl-C とも整合
  • daemon 権限不要 (--user)

実測: bash -c 'sleep 30 & sleep 30 & wait' も ros2 topic pub -r 5 も、時間経過後の残存プロセスゼロ。

ラッパー例#

#!/bin/bash
# rosprobe <max_seconds> <command...>
set -euo pipefail
MAX="$1"; shift
exec systemd-run --user --scope --collect --quiet -p "RuntimeMaxSec=${MAX}" -- "$@"

併用すべき分離策 (混信の構造的防止)#

  • 検証は ROS_DOMAIN_ID を稼働スタックと分ける (同名ノード二重化で購読先が不定になる)
  • プローブ用トピック名は毎回変える (/probe3/... 等)

関連#

  • [[agent-ros2-debug-loop]] — エージェントが短命プローブを多用する構成。この掃除法が前提
最終更新:

On this page