91
社区成员
发帖
与我相关
我的任务问题:调试建图参数等场景等需要彻底清除当前运行的ros2节点,配好参数重新启动ros2节点,但是ros节点启动数比较多,且清除ros2节点进程号后还存留ros2节点无法彻底清除。
解决方法:核心原理是清理共享内存 (SHM)。ROS 2 的默认中间件(如 FastDDS, CycloneDDS)为了实现高性能,使用了 共享内存(Shared Memory) 进行进程间通信(IPC)。如果 ROS 2 进程异常崩溃(Crash),它在 /dev/shm 目录下创建的段文件(Segment Files)不会被自动回收。下次启动时,新进程会尝试申请同名的内存段,从而触发 Failed to create segment 或内存溢出错误。以下的脚本通过“进程终止”与“资源释放”两手抓,解决由于程序异常崩溃导致的共享内存(Shared Memory)残留和节点僵死问题。
#!/usr/bin/env bash
set -euo pipefail
# ==========================================
# 参数配置
# ==========================================
DRY_RUN=0
FORCE=0
SIGNAL="INT" # ROS2 推荐使用 SIGINT
TIMEOUT_SEC=5
usage() {
echo "用法: $0 [选项]"
echo "选项:"
echo " --dry-run 仅显示将要执行的操作(不杀进程,不删文件)"
echo " --force 超时后强制发送 SIGKILL"
echo " --signal S 指定信号 (默认: INT, 可选 TERM/KILL)"
echo " --timeout N 等待秒数 (默认: 5)"
exit 0
}
# 解析参数
while [[ $# -gt 0 ]]; do
case "$1" in
--dry-run) DRY_RUN=1; shift ;;
--force) FORCE=1; shift ;;
--signal) SIGNAL="${2:-}"; shift 2 ;;
--timeout) TIMEOUT_SEC="${2:-}"; shift 2 ;;
-h|--help) usage ;;
*) echo "未知参数: $1"; usage ;;
esac
done
echo "=== 开始执行 ROS 2 环境清理 ==="
# 1. 尝试停止 ROS 2 Daemon
if command -v ros2 >/dev/null 2>&1; then
if [[ "$DRY_RUN" -eq 1 ]]; then
echo "[Dry-run] 将停止 ROS 2 Daemon"
else
echo "停止 ROS 2 Daemon..."
ros2 daemon stop >/dev/null 2>&1 || true
fi
fi
# 2. 收集需要杀死的 PIDs
DECLARE_PIDS=()
# A. 从日志获取 PID
LATEST_LOG=$(ls -td /tmp/ros2_nav_* 2>/dev/null | head -1 || true)
if [[ -n "$LATEST_LOG" && -f "$LATEST_LOG/pids.txt" ]]; then
echo "发现日志 PID 文件: $LATEST_LOG/pids.txt"
mapfile -t FILE_PIDS < "$LATEST_LOG/pids.txt"
DECLARE_PIDS+=("${FILE_PIDS[@]}")
fi
# B. 基于正则匹配所有 ROS 2 相关进程
ROS2_REGEX='ros2|/opt/ros/|rviz2|nav2_|slam_toolbox|chassis_controller|lazer_node|pose_node|explore|controller_node|cyclonedds|fastrtps|fastdds|iceoryx'
MAPFILE_PIDS=($(ps -eo pid=,args= | awk -v re="$ROS2_REGEX" -v self="$$" '$0 ~ re { if ($1 != self) print $1 }'))
DECLARE_PIDS+=("${MAPFILE_PIDS[@]}")
# 去重并验证 PID 是否存在
FINAL_PIDS=($(echo "${DECLARE_PIDS[@]}" | tr ' ' '\n' | sort -u | xargs -I{} sh -c "kill -0 {} 2>/dev/null && echo {}" || true))
if [[ ${#FINAL_PIDS[@]} -eq 0 ]]; then
echo "未发现运行中的相关进程。"
else
echo "待处理进程数: ${#FINAL_PIDS[@]}"
if [[ "$DRY_RUN" -eq 1 ]]; then
ps -o pid=,args= -p "${FINAL_PIDS[@]}"
echo "[Dry-run] 进程清理跳过"
else
# 3. 发送信号
echo "发送 SIG${SIGNAL} 信号..."
kill -s "$SIGNAL" "${FINAL_PIDS[@]}" 2>/dev/null || true
# 4. 等待退出
echo "等待进程退出 (超时: ${TIMEOUT_SEC}s)..."
end=$((SECONDS + TIMEOUT_SEC))
while (( SECONDS < end )); do
still_running=0
for pid in "${FINAL_PIDS[@]}"; do
if kill -0 "$pid" 2>/dev/null; then ((still_running++)); fi
done
if [[ $still_running -eq 0 ]]; then break; fi
sleep 0.5
done
# 5. 强制杀掉残留
if [[ "$FORCE" -eq 1 ]]; then
for pid in "${FINAL_PIDS[@]}"; do
if kill -0 "$pid" 2>/dev/null; then
echo "强制杀掉残留进程: $pid"
kill -9 "$pid" 2>/dev/null || true
fi
done
fi
fi
fi
# 6. 核心优化:清理共享内存段与 IPC 残留
# 这是解决 "Failed to create segment / No such file or directory" 的关键
echo "清理共享内存与 IPC 残留..."
# 定义需要清理的文件模式
# fastrtps: FastDDS 默认前缀
# iceoryx: 高级共享内存管理前缀
# rtps: 通用实时传输协议残留
SHM_PATTERNS=("fastrtps_*" "iceoryx_*" "rtps_*")
if [[ "$DRY_RUN" -eq 1 ]]; then
for pattern in "${SHM_PATTERNS[@]}"; do
found=$(find /dev/shm -name "$pattern" 2>/dev/null)
if [[ -n "$found" ]]; then
echo "[Dry-run] 将删除共享内存文件: $found"
fi
done
else
for pattern in "${SHM_PATTERNS[@]}"; do
# 使用 -user 过滤避免权限警告,使用 sudo 确保彻底清理(可选)
find /dev/shm -name "$pattern" -user "$(whoami)" -delete 2>/dev/null || true
done
echo "共享内存清理完成。"
fi
# 7. 附加操作:清理 ROS 2 锁文件与本地日志缓存 (可选)
if [[ "$DRY_RUN" -eq 0 ]]; then
echo "清理本地节点锁文件..."
rm -rf ~/.ros/log/* 2>/dev/null || true
fi
echo "=== 环境清理完成 ==="
第一遍运行该脚本后可能会报共享内存错误,再次直接运行该脚本,正常情况下会返回"=== 环境清理完成 ==="。
可以通过ros2 node list查看当前的ros2节点运行情况,正常情况下本机的ros2 node list应该返回空。
感谢分享
感谢分享!!👍
91
社区成员
发帖
与我相关
我的任务加载中
「智能机器人开发者大赛」官方平台,致力于为开发者和参赛选手提供赛事技术指导、行业标准解读及团队实战案例解析;聚焦智能机器人开发全栈技术闭环,助力开发者攻克技术瓶颈,促进软硬件集成、场景应用及商业化落地
试试用AI创作助手写篇文章吧