如何终止由后台运行的Bash脚本启动的ROS2节点?
解决ROS2脚本后台启动后无法终止所有节点的问题
这个问题我之前在部署多组ROS2节点时也碰到过,核心原因是你保存的PID只是bash脚本本身的进程ID,但ROS2节点(不管是用ros2 run还是launch启动的)都是脚本的子进程。默认情况下,kill -TERM只会终止脚本进程,不会自动把信号传递给它的子进程,导致节点还在后台运行。下面给你几个可靠的解决方案:
方法1:使用进程组(Process Group)批量终止
所有由脚本启动的进程默认会属于同一个进程组,进程组ID(PGID)就是脚本的PID。我们可以通过终止整个进程组来一次性杀掉所有相关节点:
启动命令(修改后)
./${SCRIPTFILE} > $LOGFILE & # 获取脚本的进程组ID(和脚本PID一致) PGID=$(ps -o pgid= $! | grep -o '[0-9]*') echo $PGID > $PIDFILE
停止命令(修改后)
# 负号表示终止整个进程组,而非单个进程 kill -TERM -$(cat $PIDFILE) 2>/dev/null # 清理PID文件(可选) rm -f $PIDFILE
这个方法最直接,能确保所有由该脚本启动的子进程(包括嵌套的ROS2节点进程)都被终止。
方法2:在脚本内部添加信号捕获,主动清理子进程
修改你的ROS2启动脚本,让它在收到终止信号时,主动杀掉所有自己启动的子进程。在脚本的开头添加以下代码:
# 当脚本收到EXIT/TERM/INT信号时,终止自己的进程组(所有子进程) trap 'kill -- -$$' EXIT TERM INT
$$代表脚本自身的PID,kill -- -$$就是终止整个进程组。这样你原来的启动和停止命令不需要修改:
- 启动:
./${SCRIPTFILE} > $LOGFILE & echo $! > $PIDFILE - 停止:
kill -TERM $(cat $PIDFILE) 2>/dev/null
当你kill脚本PID时,脚本会触发trap语句,自动清理所有子节点进程。
方法3:通过父PID筛选并终止子进程
如果上面的方法不适用,你可以通过pgrep找到所有父PID为脚本PID的进程,逐一终止:
停止命令
# 先杀掉所有子进程 kill -TERM $(pgrep -P $(cat $PIDFILE)) 2>/dev/null # 再杀掉脚本本身 kill -TERM $(cat $PIDFILE) 2>/dev/null rm -f $PIDFILE
不过这个方法可能存在遗漏(比如ROS2节点又启动了更深层的子进程),不如前两种方法可靠,只推荐作为备选。
内容的提问来源于stack exchange,提问作者Marvin-wtt
相关产品推荐
相关产品推荐

