【问题标题】:How to prevent SIGINT signal kills sub processes in Java如何防止 SIGINT 信号杀死 Java 中的子进程
【发布时间】:2019-05-26 13:24:43
【问题描述】:

我正在使用Java exec 运行bash 脚本,但是当我输入CTRL+C 时,Java 进程和子进程都会退出,如何在JVM 关闭后让子进程继续运行?

String command = "ffmpeg -re -strict -2 -i video.mp4 -c:v copy -an -f rtp rtp://127.0.0.1:1234";
Utils.writeToFile("sub.sh", command);

new ProcessBuilder("parent.sh", "sub.sh").inheritIO()
        .start().waitFor();

parent.sh:

#!/bin/bash
trap '' SIGINT SIGTERM SIGQUIT SIGHUP SIGTSTP
nohup "$@" &

我已经阅读了herehere 类似问题的答案,例如,使用 nohup 在该脚本运行命令中启动父 bash 脚本,或使用陷阱命令来阻止信号, 在我的研究中,它适用于例如“tail -f somefile”,但不适用于我的用例 “ffmpeg -params”,请帮忙,谢谢。

【问题讨论】:

    标签: java exec


    【解决方案1】:

    TL;DR

    在您的parent.sh 中使用setsid 而不是nohup

    #!/bin/bash
    setsid $@ &
    

    第一个问题是当你的 Java 程序接收到一个SIGTERM 信号时(在CTRL + C 之后),JVM 向子进程发送了一个SIGKILL 信号,它不能被捕获、阻塞或忽略。因此,一旦您的parent.sh 进程收到SIGKILL,它就会被传递给它的子进程sub.sh,从而停止该进程。

    第二个问题是nohup "$@" & 你让你的sub.sh 子程序在后台运行,但它并没有完全脱离当前终端。 nohup 所做的基本上是忽略 SIGHUP 信号。它正常执行进程(在后台),实际上并不提供守护进程。

    您可以使用setsid 代替nohup,它通过创建一个新会话来分离子进程sub.sh,该会话将没有控制终端,因此不会收到SIGKILL 信号。

    另一种方法可能是使用disown,例如source $@ & disown,但在这种情况下,它不会阻止正在运行parent.sh 的当前终端。问题是,您将无法知道子进程何时完成。所以你需要做更多的工作来监控子进程ID。

    补充说明:

    【讨论】: