【问题标题】:Terminating QProcess doesn't kill child process终止 QProcess 不会杀死子进程
【发布时间】:2019-06-05 03:30:35
【问题描述】:

我有以下问题: 我有一个 Qt GUI-Application (c++),它通过 bash 脚本启动一个外部 java 应用程序。当我终止我的 QProcess 时,脚本被杀死,但子进程(java 应用程序)仍在运行。

我不理解这种行为,因为如果我在终端中运行脚本并杀死它,子进程也会被杀死。可能和Qt中的各种事件循环有关,我还没弄明白。


这是我的代码:

MainWindow.h

#include <QMainWindow>
#include <QProcess>

namespace Ui {
  class MainWindow;
}

class MainWindow : public QMainWindow
{
  Q_OBJECT
public:
  explicit MainWindow(QWidget *parent = nullptr);
  ~MainWindow();

private slots:
  void slot_startQProcess();
  void slot_killQProcess();

private:
  Ui::MainWindow *ui;

  QProcess myProcess;
};

MainWindow.cpp

#include "MainWindow.h"
#include "ui_MainWindow.h"

MainWindow::MainWindow(QWidget *parent) :
  QMainWindow(parent),
  ui(new Ui::MainWindow)
{
  ui->setupUi(this);
  connect(ui->pb_start, &QPushButton::clicked, this, &MainWindow::slot_startQProcess);
  connect(ui->pb_kill, &QPushButton::clicked, this, &MainWindow::slot_killQProcess);
}

MainWindow::~MainWindow()
{
  delete ui;
}

void MainWindow::slot_startQProcess()
{
  myProcess.setWorkingDirectory("./i2exrep");
  myProcess.start("./myScript.sh");
}

void MainWindow::slot_killQProcess()
{
  myProcess.close();
}

main.cpp

#include "MainWindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
  QApplication a(argc, argv);
  MainWindow w;
  w.show();
  return a.exec();
}

myScript.sh

#!/bin/csh
java -jar i2exrep.jar

文件夹结构:

  • .pro 文件
  • *.cpp
  • *.h
  • i2exrep(文件夹)
    • myScript.sh

所以我的应用程序启动“myScript.sh”,它启动“java -jar i2exrep.jar”

杀死 QProcess 只会终止“myScript.sh”,但“java -jar i2exrep.jar”仍在运行。 - i2exrep.jar


编辑:

我尝试了另一个主函数:

#include <QCoreApplication>
#include <QProcess>
int main(int argc, char *argv[])
{
  QCoreApplication a(argc, argv);
  QProcess myProcess;
  myProcess.setWorkingDirectory("./i2exrep");
  myProcess.start("./myScript.sh");
  return a.exec();
}

当我退出这个程序时,Java 应用程序也会终止。当我完成第一个程序(GUI 应用程序)时,只有脚本被终止,Java 应用程序继续运行。我收到以下错误消息:

QProcess: Destroyed while process ("./myScript.sh") is still running.

【问题讨论】:

  • 您是否在关闭前尝试使用QProcess::killQProcess::terminate? (发送后您必须稍等片刻才能真正退出。使用QProcess::waitForFinished 这样做)
  • 我的猜测是 myScript.sh 正在启动分离的 i2exrep.jar,因此它可以在生成过程终止后运行。
  • @Felix:我将插槽编辑如下:void MainWindow::slot_killQProcess() { myProcess.kill(); myProcess.terminate(); myProcess.waitForFinished(1000); myProcess.close(); } 调用该方法仍然只停止脚本。 Java 应用程序仍在运行。
  • @Mike 这也是我的猜测。但是如何防止 Java 应用程序分离运行呢?我想将 Java 应用程序“链接”到父进程 myScript.sh.
  • kill (SIGKILL) 直接进程,所以 terminate (SIGTERM) 和 close (关闭/关闭流) 无效。你应该只terminate

标签: c++ qt


【解决方案1】:

我假设您运行一些 UNIX 系统(因为 shell 和 / 路径分隔符)。

你真的需要两个进程(shell 和 java)吗?也许只留下java会解决你的问题。将exec 添加到您的脚本中:

#!/bin/csh
exec java -jar i2exrep.jar

我的猜测是,当您在终端中运行应用程序然后关闭它时,所有子进程都会收到 SIGHUP,因为它们失去了控制终端。当您从 GUI 应用程序运行脚本时,首先没有控制终端,因此 java 不会在退出时终止。无论如何,终止子进程的正确方法是向它发送一个信号(SIGTERM,一段时间后可能是 SIGKILL),然后等待(2)它的终止。

如果您不想摆脱进程链中的 shell 进程,请参阅 this answer 了解如何将信号转发到 shell 中的子进程。

【讨论】:

  • 是的,我运行的是 UNIX 系统。对不起,我忘了提。
  • 我使用脚本的唯一原因是我不想这样调用我的 QProcess:myProcess.start("java", "-jar i2exrep.jar") 我使用 QProcess 启动了其他几个应用程序,这就是为什么我找到程序名称“java”有点不适合这个应用程序。
  • 好吧,可以通过包装脚本运行 java,但 IMO 最好避免应用程序的不必要的“代理”子级(即 /bin/csh)。您可以在脚本中的行前添加“exec”,之后杀死您的子进程将杀死 java,而不是“代理”shell。这是因为“exec”将正在运行的进程(shell)的地址空间替换为正在执行的程序的代码和数据,并且子进程的PID保持不变(即只有一个fork发生)。
【解决方案2】:

我通过使用setsid 启动进程来解决它。

请检查以下示例:

process->start("setsid ./example.sh");
(...)
QString killingProcess = "kill -TERM -" + QString::number(process->pid());
system(killingProcess.toStdString().c_str());

更多详情,请访问this链接。

【讨论】: