You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JNI中创建ROS NodeHandle致程序无法响应SIGINT正常退出

根本原因

这个问题和CppBridge实例销毁阻塞没有关系,核心是roscpp(ROS C++客户端库)的默认行为和JVM的信号处理逻辑冲突:

  • 调用ros::init时如果不传入ros::init_options::NoSigintHandler选项,roscpp会主动注册SIGINT信号处理函数,覆盖JVM原本的SIGINT响应逻辑——JVM收到SIGINT本来会触发Spring Boot的正常关闭流程、执行Bean销毁、等待所有非守护线程退出后终止进程,被roscpp覆盖后,SIGINT只会触发roscpp内部的关闭标志位置位,不会通知JVM启动关闭流程。
  • 创建ros::NodeHandle时,roscpp会启动全局后台线程(包括XML-RPC通信线程、回调调度线程等),这些线程会一直持有进程运行资源,因为没有触发JVM关闭流程、也没有主动调用ros::shutdown()回收这些线程,进程就会一直挂住,只能通过SIGKILL强制终止。
修复步骤
  • 第一步:修改ROS初始化逻辑,禁止roscpp接管SIGINT信号
    修改CppBridge.cpp里的ros::init调用,添加NoSigintHandler选项,把信号处理权交还给JVM:
ros::init(argc, nullptr, "ifr_mimosa_bridge", ros::init_options::NoSigintHandler);
  • 第二步:添加JNI层的ROS资源释放接口
    首先在CppBridge.java中新增native的shutdown方法:
public class CppBridge {

    static {
        System.load(new File("backend/src/main/cpp/libcppbridge.so").getAbsolutePath());
    }

    public native void start();
    public native void shutdown();
}

修改CppBridge.cpp,将NodeHandle从start方法的栈变量改为全局持有,同时实现shutdown方法主动回收ROS资源:

#include <ros/ros.h>

static ros::NodeHandle* g_privateNh = nullptr;

JNIEXPORT void JNICALL Java_org_ifremer_rospipe_CppBridge_start(JNIEnv* env, jobject obj)
{
    int argc = 0;
    ros::init(argc, nullptr, "ifr_mimosa_bridge", ros::init_options::NoSigintHandler);
    g_privateNh = new ros::NodeHandle("~");
}

JNIEXPORT void JNICALL Java_org_ifremer_rospipe_CppBridge_shutdown(JNIEnv* env, jobject obj)
{
    if (ros::ok()) {
        ros::shutdown();
        // 阻塞等待所有ROS后台线程完全退出
        ros::waitForShutdown();
    }
    if (g_privateNh) {
        delete g_privateNh;
        g_privateNh = nullptr;
    }
}
  • 第三步:绑定Spring Boot生命周期,在应用关闭时主动释放ROS资源
    修改RosPipe.java,添加@PreDestroy注解的销毁方法,在Spring容器关闭时调用ROS的shutdown逻辑:
@Component
public class RosPipe {

    private final CppBridge mCppBridge = new CppBridge();

    @PostConstruct()
    public void onStart() {
        mCppBridge.start();
    }

    @PreDestroy
    public void onStop() {
        mCppBridge.shutdown();
    }
}
额外注意

如果后续需要处理ROS订阅/服务回调,不要直接在start方法中调用ros::spin()阻塞Spring启动线程,建议单独开一个C++后台线程执行spin逻辑,在shutdown流程中ros::shutdown()会自动终止该线程。

内容的提问来源于stack exchange,提问作者Tim Autin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 00:48:30