Как закрыть дочерний процесс Qt и заставить дочерний процесс выполнить код очистки?

Я запускаю процесс в Linux/Qt, а затем запускаю некоторые дочерние процессы с использованием QProcess. Затем, в конце концов, я хочу изящно закрыть дочерние процессы (например, выполнить некоторый код очистки).

Дочерние процессы используют QSharedMemory, и прямо сейчас, когда я вызываю QProcess::close( ) дочерние процессы закрываются без вызова QSharedMemory::detach() и в результате все процессы закрываются... но остается не очищенная общая память.

У меня есть код дочернего процесса, и в коде есть функция cleanup(). Как родительский процесс закрывает QProcess таким образом, чтобы дочерний процесс выполнял cleanup()?


person Trevor Boyd Smith    schedule 21.09.2011    source источник
comment
Есть много разных способов сделать это. Цель здесь такова: я хотел увидеть, каков общий или лучший подход, который используется в среде Qt.   -  person Trevor Boyd Smith    schedule 21.09.2011
comment
К вашему сведению, все процессы подключены к одному и тому же общему dbus.   -  person Trevor Boyd Smith    schedule 21.09.2011
comment
Я не мастер Qt. Поэтому я оставлю это открытым, пока не получу ответ от кого-то с большим опытом работы с Qt.   -  person Trevor Boyd Smith    schedule 22.09.2011


Ответы (3)


Я заставил ребенка выполнить код очистки Qt, используя обработчики сигналов Unix.

Вот объяснение высокого уровня:

  • родитель открывает дочерний процесс, используя QProcess
  • происходит обработка
  • the parent closes the child process using QProcess::terminate() which raises the SIGTERM signal on the child
  • дочерний элемент реализует обработчик сигналов unix для SIGTERM
  • из обработчика сигнала unix qApp->exit(0); имеет место
  • qApp выдает сигнал Qt "aboutToQuit()"
  • подключите слот cleanup() дочернего процесса к сигналу qApp aboutToQuit()

Код дочернего процесса для обработки сигнала unix SIGTERM:

static void unixSignalHandler(int signum) {
    qDebug("DBG: main.cpp::unixSignalHandler(). signal = %s\n", strsignal(signum));

    /*
     * Make sure your Qt application gracefully quits.
     * NOTE - purpose for calling qApp->exit(0):
     *      1. Forces the Qt framework's "main event loop `qApp->exec()`" to quit looping.
     *      2. Also emits the QCoreApplication::aboutToQuit() signal. This signal is used for cleanup code.
     */
    qApp->exit(0);
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    MAINOBJECT mainobject;

    /*
     * Setup UNIX signal handlers for some of the common signals.
     * NOTE common signals:
     *      SIGINT:     The user started the process on the command line and user ctrl-C.
     *      SIGTERM:    The user kills the process using the `kill` command.
     *                  OR
     *                  The process is started using QProcess and SIGTERM is
     *                  issued when QProcess::close() is used to close the process.
     */
    if (signal(SIGINT, unixSignalHandler) == SIG_ERR) {
        qFatal("ERR - %s(%d): An error occurred while setting a signal handler.\n", __FILE__,__LINE__);
    }
    if (signal(SIGTERM, unixSignalHandler) == SIG_ERR) {
        qFatal("ERR - %s(%d): An error occurred while setting a signal handler.\n", __FILE__,__LINE__);
    }
    // executes mainbobject.cleanupSlot() when the Qt framework emits aboutToQuit() signal.
    QObject::connect(qApp,          SIGNAL(aboutToQuit()),
                     &mainobject,   SLOT(cleanupSlot()));

    return a.exec();
}

Вывод:

Я подтвердил, что это решение работает.

Я думаю, что это хорошее решение, потому что:

  • пусть родитель закроет дочерний процесс таким образом, чтобы дочерний процесс выполнил очистку
  • если родительский процесс закрывается по ошибке и оставляет дочерний процесс работающим, пользователь/сисадмин может убить оставшийся дочерний процесс с помощью команды kill, и дочерний процесс все равно очистит себя перед закрытием

p.s. «Почему бы просто не сделать код очистки непосредственно в точке входа обработчика сигнала?»

Короткий ответ: потому что вы не можете. Вот объяснение того, почему вы не можете выполнить свой код очистки Qt в функции обработчика сигналов unix. Из документации Qt "Вызов функций Qt из обработчиков сигналов Unix":

Вы не можете вызывать функции Qt из обработчиков сигналов Unix. Применяется стандартное правило POSIX: вы можете вызывать функции, безопасные для асинхронных сигналов, только из обработчиков сигналов. Полный список функций, которые вы можете вызывать, см. в разделе Signal Actions. из обработчиков сигналов Unix.

person Trevor Boyd Smith    schedule 21.09.2011
comment
но я новичок в Qt... поэтому я не уверен, что это обычный способ ведения дел. или, может быть, есть более элегантное решение. - person Trevor Boyd Smith; 22.09.2011
comment
Небезопасно вызывать qApp->exit() в обработчике сигнала, если только вы не проведете аудит кода, чтобы убедиться, что в реализации на вашей платформе используется только асинхронно-безопасное подмножество POSIX API. - person Kuba hasn't forgotten Monica; 12.09.2015

Вы можете попробовать подключить сигнал processExited() к слоту для самостоятельной обработки отсоединения от общей памяти, насколько мне известно, в QProcess нет ничего, что напрямую запускало бы метод отсоединения.

person Nicholas Smith    schedule 21.09.2011
comment
Дочерний процесс — это тот, кто должен вызвать выполнение кода cleanup(). - person Trevor Boyd Smith; 21.09.2011
comment
Вы предлагаете, чтобы родительский close() QProcess выполнял некоторый cleanup() код при закрытии QProcess? - person Trevor Boyd Smith; 21.09.2011
comment
Ну, это или или, я бы сказал, что, вероятно, безопаснее, чтобы родитель закрывал QProcess в конце выполнения, а затем занимался освобождением самой памяти, так как тогда вы можете реализовать такие вещи, как сохранение счетчиков, поэтому нет возможности что-то выпустить это необходимо. - person Nicholas Smith; 21.09.2011
comment
В этом конкретном случае родитель не может выполнить очистку. Ребенок должен, потому что ребенок использует [QSharedMemory](QSharedMemory): Unix: QSharedMemory владеет сегментом общей памяти. Когда последний поток или процесс, имеющий экземпляр QSharedMemory, присоединенный к конкретному сегменту разделяемой памяти, отсоединяется от сегмента, уничтожая свой экземпляр QSharedMemory, ядро ​​Unix освобождает сегмент разделяемой памяти. Но если этот последний поток или процесс рухнет, не запустив деструктор QSharedMemory, сегмент разделяемой памяти переживет крах. - person Trevor Boyd Smith; 21.09.2011
comment
Ах, ха, а где создается общая память? - person Nicholas Smith; 21.09.2011

Я считаю, что вам нужно реализовать небольшой протокол здесь. Вам нужен способ сообщить дочернему процессу, чтобы он завершился изящно. Если у вас есть исходный код для обоих, вы можете попытаться реализовать такой сигнал, используя QtDBus библиотека.

person cyco130    schedule 21.09.2011