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

如何fork Qt6程序?困境下的技术探索与实现难题

在Qt应用中动态创建新运行实例的探索(fork方式)

我已经通过谷歌全面搜索过这个主题,得到的结论是“不要这么做”。但我目前进退两难,所以打算在彻底放弃“fork Qt应用”这个思路前,把它作为深入理解Qt初始化本质的练习,彻底探索一遍。

前提需求

我需要动态创建当前应用的新运行实例,新实例需与父实例同时运行。

我希望实现一个用于fork当前运行应用的函数,它需足够鲁棒,能处理所有可能存在的状态:

  • 应用的实例化方式未知,最常见的两种是:应用主函数在QTest::testCase()中实例化,或在int main(int argc, char *argv[])入口中实例化。
  • 未知Qt事件循环是否已启动(通过QApplication/QTest::qExec等方式)。
  • 未知运行的操作系统或使用的图形子系统。
  • 未知应用目前已交互过的dbus、文件句柄、OpenGL上下文、信号或其他资源。
  • fork发生后,父应用需继续无中断运行。
  • 子进程需安全重置状态,通过实例化QApplication并构建UI等方式重新开始。

初步思路

  1. 父Qt6应用已处于运行状态,且已建立了复杂的状态。
  2. 对应用执行fork操作:
    • 父进程保持原有状态不变。
    • 子进程拥有父进程所有状态的过期副本。
  3. 在子进程中关闭所有过期状态。
  4. 启动新的QApplication并设置子进程所需的状态。

遇到的核心问题

该方法存在诸多问题,但核心问题在于:逐一追踪并成功关闭所有资源就像打地鼠游戏一样永无止境,尤其跨平台和不同Qt版本下,成功的可能性微乎其微。我的初步实现就完全体现了这一点:在清理旧状态后,子进程中实例化QPushButton时会永久挂起。

后续思路探索

我的下一个思路是通过某种方式复制或调用exit()系统调用的代码,让其运行到操作系统终止进程的阶段。似乎有大量exec*()、fork*()和posix_*()调用可实现类似功能,但这会牺牲平台独立性。最坏情况下,我们会采用这种方式,为所有关注的平台实现平台相关的方法。

请注意,使用QProcess不可行,因为我们要运行的代码与当前运行的程序并非独立可执行文件,且我们无法控制父进程的包装/实例化方式(例如代码可能在接管了命令行参数的QTest中启动)。

初步尝试代码

testFork.hpp

#ifndef TESTFORK_HPP
#define TESTFORK_HPP

#include "test/Common.hpp"

class TestFork:public QObject
{
    Q_OBJECT
private slots:
    void test();
};

#endif
// TESTFORK_HPP

testFork.cpp

#include "TestFork.hpp"

#include <QDebug>
#include <QApplication>
#include <QPushButton>
#include <QTimer>
#include <QAbstractEventDispatcher>
#include <QOpenGLContext>
#include <QtDBus/QDBusConnection>

#include <csignal>
#include <unistd.h> // For fork
#include <sys/wait.h> // For waitpid


void TestFork::test()
{
    qDebug() << "TEST FORK";
    
    pid_t pid = fork();
    
    if (pid < 0)
    {
        qCritical() << "Fork failed!";
        return;
    }
    else if (pid == 0)
    {
        qDebug() << "In child process A1a";
        
        QDBusConnection::disconnectFromBus("session");
        qDebug() << "In child process A1b";
        QDBusConnection::disconnectFromBus("system");

        // Reset OpenGL contexts if they exist
        if (QOpenGLContext::currentContext()) {
            qDebug() << "In child process A6a";
            QOpenGLContext::currentContext()->doneCurrent();
            qDebug() << "In child process A6b";
            QOpenGLContext::currentContext()->deleteLater();
        }
        
        
        qDebug() << "In child process A7a";
        // Reset signal handlers to default
        signal(SIGPIPE, SIG_DFL);
        qDebug() << "In child process A7b";
        signal(SIGCHLD, SIG_DFL);
        qDebug() << "In child process A8";
        
        /* Commented out so we can see log output but does not seem to make a difference.
        close(0); // Close standard input
        close(1); // Close standard output
        close(2); // Close standard error
*/
        qDebug() << "In child process A9";
        for (int fd = 3; fd < 1024; ++fd) {
            close(fd);
        }
        
        if (QAbstractEventDispatcher::instance() != nullptr)
        {
            qDebug() << "An existing event loop is running, exiting it.";
            QCoreApplication::exit();  // End the existing event loop if running
        }
        
        
        int argc = 0;
        char **argv = nullptr;
        qDebug() << "In child process B1";
        QApplication childApp(argc, argv);
        qDebug() << "In child process B2";
        QPushButton *button = new QPushButton("Terminate Child");
        qDebug() << "In child process C";
        
        
        QObject::connect(button, &QPushButton::clicked, &childApp, &QApplication::quit);
        qDebug() << "In child process D";
        
        QTimer::singleShot(100, [button]() {
            qDebug() << "In child process E";
            button->show();
            qDebug() << "In child process F";
        });
        
        qDebug() << "In child process G";
        childApp.exec();
        qDebug() << "In child process H";
        
        _exit(0);  // Ensure clean exit of the child process
    }
    else
    {
        qDebug() << "In parent process";
        int status;
        waitpid(pid, &status, 0);
    }
}

内容的提问来源于stack exchange,提问作者Mr. Developerdude

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 23:24:51