Как автоматически уничтожить дочерние процессы в Windows? - PullRequest
63 голосов
/ 10 сентября 2008

В приложении C ++ для Windows я запускаю несколько долго работающих дочерних процессов (в настоящее время я использую CreateProcess (...) для этого.

Я хочу, чтобы дочерние процессы автоматически закрывались , если мои основные процессы зависали или закрывались.

Из-за требования, что это должно работать для сбоя «родителя», я считаю, что это должно быть сделано с использованием некоторого API / функции операционной системы. Так что все "дочерние" процессы очищены.

Как мне это сделать?

Ответы [ 7 ]

70 голосов
/ 10 сентября 2008

Windows API поддерживает объекты, называемые «Объекты заданий». Следующий код создаст «задание», настроенное на останов всех процессов, когда основное приложение заканчивается (когда очищаются его дескрипторы). Этот код должен быть запущен только один раз.

HANDLE ghJob = CreateJobObject( NULL, NULL); // GLOBAL
if( ghJob == NULL)
{
    ::MessageBox( 0, "Could not create job object", "TEST", MB_OK);
}
else
{
    JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = { 0 };

    // Configure all child processes associated with the job to terminate when the
    jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
    if( 0 == SetInformationJobObject( ghJob, JobObjectExtendedLimitInformation, &jeli, sizeof(jeli)))
    {
        ::MessageBox( 0, "Could not SetInformationJobObject", "TEST", MB_OK);
    }
}

Затем, когда каждый дочерний процесс создан, выполните следующий код, чтобы запустить каждый дочерний процесс и добавить его в объект задания:

STARTUPINFO info={sizeof(info)};
PROCESS_INFORMATION processInfo;

// Launch child process - example is notepad.exe
if (::CreateProcess( NULL, "notepad.exe", NULL, NULL, TRUE, 0, NULL, NULL, &info, &processInfo))
{
    ::MessageBox( 0, "CreateProcess succeeded.", "TEST", MB_OK);
    if(ghJob)
    {
        if(0 == AssignProcessToJobObject( ghJob, processInfo.hProcess))
        {
            ::MessageBox( 0, "Could not AssignProcessToObject", "TEST", MB_OK);
        }
    }

    // Can we free handles now? Not sure about this.
    //CloseHandle(processInfo.hProcess); 
    CloseHandle(processInfo.hThread);
}

VISTA ПРИМЕЧАНИЕ. См. . AssignProcessToJobObject всегда возвращает «отказ в доступе» в Vista , если у вас возникают проблемы с доступом с AssignProcessToObject () в Vista.

5 голосов
/ 10 сентября 2008

Одним из несколько хакерских решений было бы присоединение родительского процесса к каждому дочернему элементу в качестве отладчика (используйте DebugActiveProcess ). Когда отладчик завершает работу, все процессы отладчика также завершаются.

Лучшее решение (при условии, что вы также написали дочерние процессы) состояло бы в том, чтобы дочерние процессы контролировали родительский процесс и выходили, если он исчезал.

3 голосов
/ 10 сентября 2008

Windows Job Objects звучит как хорошее место для начала. Имя объекта задания должно быть общеизвестным или передаваться дочерним элементам (или наследовать дескриптор). Дети должны были быть уведомлены, когда родитель умирает, либо из-за неудачного «сердцебиения» IPC, либо просто через WFMO / WFSO на дескрипторе процесса родителя. В этот момент любой дочерний процесс может TermianteJobObject отключить всю группу.

0 голосов
/ 10 сентября 2008

С макушки головы:

  • Рассматривали ли вы использование потоков вместо процессов?
  • Попробуйте передать дескриптор основного потока / процесса дочерним процессам и заставить их ждать этого дескриптора. Это работает для потоков, так как ожидание на дескрипторе потока ожидает, пока этот поток не завершится и не завершится. Не слишком уверен, что он будет работать для процессов, следует проверить MSDN, чтобы проверить это.
0 голосов
/ 10 сентября 2008

Вы можете инкапсулировать каждый процесс в объекте C ++ и хранить их список в глобальной области видимости. Деструкторы могут закрыть каждый процесс. Это будет работать нормально, если программа завершится нормально, но в случае сбоя все ставки выключены.

Вот пример:

class myprocess
{
public:
    myprocess(HANDLE hProcess)
        : _hProcess(hProcess)
    { }

    ~myprocess()
    {
        TerminateProcess(_hProcess, 0);
    }

private:
    HANDLE _hProcess;
};

std::list<myprocess> allprocesses;

Тогда всякий раз, когда вы запускаете его, вызывайте allprocessess.push_back (hProcess);

0 голосов
/ 10 сентября 2008

Вы можете сохранить отдельный процесс сторожевого таймера. Его единственная задача - наблюдать за текущим пространством процесса, чтобы определить ситуации, которые вы описываете. Он может даже перезапустить исходное приложение после сбоя или предоставить пользователю различные параметры, собрать отладочную информацию и т. Д. Просто постарайтесь сделать его достаточно простым, чтобы вам не понадобился второй сторожевой таймер для просмотра первого. 1001 *

0 голосов
/ 10 сентября 2008

Возможно, вам придется вести список процессов, которые вы запускаете, и уничтожать их один за другим при выходе из программы. Я не уверен в специфике этого в C ++, но это не должно быть сложно. Трудной частью, вероятно, будет обеспечение того, чтобы дочерние процессы были закрыты в случае сбоя приложения. .Net имеет возможность добавить функцию, которая вызывается при возникновении необработанного исключения. Я не уверен, что C ++ предлагает те же возможности.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...