【问题标题】:QProcess::kill() does not kill children in linuxQProcess::kill() 不会杀死linux中的孩子
【发布时间】:2015-03-03 10:59:24
【问题描述】:

我使用QProcess 在服务器上进行冗长的计算。只要我让它自己完成,它可能只需要几秒钟或几个小时,并且工作正常。但是,我需要有可能在它完成之前终止进程,这只能在我的 Windows 机器上正常工作。

在 linux 中,我的QProcess 被杀死,我的数据处理成功执行,但由它产生的子进程仍然存在。这是我的代码的摘录:

// constructor
server::server(QObject* parent) : QTcpServer(parent)
{
    this->calc_proc = new QProcess(this);
    QObject::connect(this->calc_proc, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(calc_finished(int,QProcess::ExitStatus)));

    ...
}

void server::start_job(){

    QString working_directory = "...";
    QString input_file = "...";
    QStringList arguments;
    arguments << "memory=max" << "batch=no" << input_file;

    this->calc_proc->setWorkingDirectory(working_directory);
    this->calc_proc->start("path_to_external_program", arguments);

    qDebug() << "Calc started with pid: " << this->calc_proc->pid();
}

void server::kill_job(){

    this->calc_proc->terminate();
    //this->calc_proc->kill();
}

使用terminate()kill() 时的行为似乎没有什么不同。据我所知,子进程是我的进程的子进程:

Calc started with pid:  26395

ps ax
...
26441 pts/0    S      0:00 time /msc/MSC_Nastran/20131/msc20131/linux64/analysis
26442 pts/0    Rl    16:59 /msc/MSC_Nastran/20131/msc20131/linux64/analysis
...

ps -p 26442 -o ppid=
26441

ps -p 26441 -o ppid=
26395

我的QProcess 返回他的 pid 为 26395,它有一个孩子 26441,有一个孩子 26442。所以我希望当我杀死我的时候所有这些都被杀死。如前所述,他们幸存下来。是否也有任何独立于平台的方法来杀死这些?

【问题讨论】:

    标签: c++ qt process


    【解决方案1】:

    user4419802 让我走上了正确的道路。在 Linux 中,杀死一个进程并不会杀死它的子进程。他们被向上移动并继续没有父母。

    通常,您希望在生成孩子时保存所有 pids 的孩子(并且有多个答案告诉您如何做到这一点)但在我的情况下这是不可能的,因为我不是在生成孩子而是在外部我调用的应用程序是在我不知情的情况下这样做的。所以我想出了以下对我有用的解决方案:

    QProcess* main_process = new QProcess(this);
    ...
    
    // linux: a child is spawned which is not killed with its parent
    //        therefore the process id is retrieved and the process killed independently
    #if defined(Q_OS_LINUX)
    QProcess get_child_a;
    QStringList get_child_a_cmd;
    get_child_a_cmd << "--ppid" << QString::number(main_process->processId()) << "-o" << "pid" << "--no-heading";
    get_child_a.start("ps", get_child_a_cmd);
    get_child_a.waitForFinished(5000);
    QString child_a_str = get_child_a.readAllStandardOutput();
    int child_a = child_a_str.toInt();
    
    QProcess::execute("kill " + QString::number(child_a));
    #endif
    
    // windows & linux: kill main process
    main_process->kill();
    

    就我而言,我知道只返回一个子进程 ID,因此解析 get_child 进程输出是一个简单的转换。

    【讨论】:

    • 更干净的解决方案是在生成孩子的 PID 时保存它,而不是解析 ps 输出。无论如何,您的代码都依赖于操作系统(ps --ppid - 仅限 GNU?)。
    • 这样会更干净,但产生孩子的不是我的代码。我只是启动了一个外部程序,它在某些时候会在我不知情的情况下产生孩子。 ps --ppidUnix manpages 上,所以我希望它一般可以在 linux 中工作....在我的情况下它不需要 windows
    • 我的意思是--ppid 是 GNU 选项,所以它只适用于 GNU/Linux。 BSD 和 Solaris 等都不起作用。所以它很脏。
    • 啊,你说得对。随意建议一些更便携的东西,因为我找不到任何不依赖于操作系统的解决方案,其中父母和孩子实际上都不是你自己的代码
    【解决方案2】:

    QProcess::kill() 就是 SIGKILL。所以这不是关于 Qt,而是关于 Unix 进程管理。

    您可以阅读该问题How to make child process die after parent exits?

    【讨论】:

      【解决方案3】:

      感谢@Bowdzone 提供最初的想法!

      由于答案不是 100% 对我有用,因为我的父进程产生了多个子进程,所以我对这种方法进行了一些调整:

      void CameraReceiver::stopChildProcesses(qint64 parentProcessId) {
          qDebug() << "stopChildProcesses for parent id:" << parentProcessId;
      
          QProcess get_childs;
          QStringList get_childs_cmd;
          get_childs_cmd << "--ppid" << QString::number(parentProcessId) << "-o" << "pid" << "--no-heading";
          get_childs.start("ps", get_childs_cmd);
          get_childs.waitForFinished();
          QString childIds(get_childs.readAllStandardOutput());
          childIds.replace('\n', ' ');
      
          QProcess::execute("kill " + childIds);
      }
      

      然后我可以简单地调用以下内容:

      stopChildProcesses(parentProcess.processId());
      

      【讨论】:

      • 非常适合我。作为一种可能的升级,使其递归杀死孙子等可能会很有用。无论如何,非常感谢 Manuel =)