Как быстро завершить работу однопоточного приложения с Windows 10 параллельной загрузкой? - PullRequest
0 голосов
/ 08 января 2020

РЕДАКТИРОВАТЬ : некоторые пояснения ниже

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

В случае любого сбоя это приложение должно завершиться немедленно и максимально быстро.

if ( !success )
  ExitProcess( 1 );

Проблемы возникают с «окном» 10 поддержка загрузки параллельной библиотеки в ntdll.dll ".

Как и , описанные в этом ответе и в этой статье эта функция предотвращает быстрый сбой.

Программы, которые выполняются менее чем за 30 секунд, будут зависать из-за ntdll! TppWorkerThread ожидает ожидания простоя до завершения процесса.

image

image

Даже если я установлю MaxLoaderThreads в реестре, пул протекторов создается, когда я вызываю LoadLibrary .

image: MaxLoaderThreads in Registry

Мой вопрос как прекратить процесс без ожидания потоков-загрузчиков, без прямого вызова ZwCreateUserProcess и без изменения реестра на целевой машине.

Моя идея состоит в том, чтобы заменить ExitProcess , который вызывает TerminateProcess следующим образом (и, похоже, работает).

VOID WINAPI ExitProcessNow( UINT uExitCode )
{
  if ( TerminateProcess( GetCurrentProcess(),
                         uExitCode ) )
    ExitThread( uExitCode );
  else
    ExitProcess( uExitCode );
}

EDIT Некоторые пояснения

Предположим следующую программу:

#include <Windows.h>
#ifndef _DEBUG
#pragma comment ( linker, "/ENTRY:EntryPoint" )
#else
#define EntryPoint main
#endif

#define NUMBER_OF_WORKER_THREADS  3

DWORD __stdcall WorkerThreadProc( PVOID __unused )
{
  for ( ;; )
    Sleep( 1000 );
}

void EntryPoint()
{
  for ( int ii = NUMBER_OF_WORKER_THREADS; ii; --ii )
    CloseHandle( CreateThread( NULL, 0, WorkerThreadProc, NULL, 0, NULL ) );
  Sleep( 10000 );
  // For demonstration do not call ExitProcess.
  // ExitProcess( 0 );
}

Начальный поток запускает трех рабочих, ждет 10 секунд и затем завершается. Рабочие бегут бесконечно. Если _DEBUG не определено, то нет кода CRT, который вызывает ExitProcess в конце main(), и процесс продолжает работать с тремя потоками в WorkerThreadPro c.

Я наблюдал такую ​​ситуацию. Мой процесс и / или порожденный процесс, в котором запущены три потока с точкой входа ntdll!Tp....

Мой код окончательно называется ExitProcess (после замены ExitProcess на TerminateProcess все заработало).

Дело в том, что в случае, когда программа должна выйти, она должна выйти немедленно, независимо от каких-либо блокировок в какой-либо библиотеке или любом потоке. Может быть, это как-то связано с параллельной загрузкой или нет, может, в моем или порожденном коде такая тупик, это не имеет значения. Программа должна завершиться, и процесс должен стать сигнальным.

Основным вариантом использования этой программы является перезапуск других программ при их выходе по любой причине, как / sbin / init , когда настроен с respawn в / etc / inittab .

Ответы [ 2 ]

0 голосов
/ 08 января 2020

Я не могу воспроизвести это в данный момент, возможно, это зависит от дневного (или ночного) времени.

Эта ситуация (три потока в ntdll.dll! TpReleaseWork после выхода) не только (иногда) появляется моя программа "spawn (7) .exe", но также (иногда) для других (хорошо известных) приложений (я вижу это в Process Explorer), которые я не могу изменить. Может быть странная ситуация в «новой» функции «Поддержка параллельной загрузки библиотеки Windows 10 в ntdll.dll». Может быть, я пропустил обновление, может быть что угодно.

В любом случае, мне нужно "быстро потерпеть неудачу", и поскольку я использую процессы для синхронизации (WaitForSingleObject), у меня теперь есть две вспомогательные функции.

ExitProcessNow, чтобы "kill" себя и WaitForProcessOrMainThread для ожидания недавно созданного целевого процесса (или его основного потока) с вызовом CreateProcess.

VOID
__stdcall
ExitProcessNow( UINT uExitCode )
{
  if ( TerminateProcess( GetCurrentProcess(),
                         uExitCode ) )
    ExitThread( uExitCode );
  else
    ExitProcess( uExitCode );
}

VOID
__stdcall
WaitForProcessOrMainThread( PPROCESS_INFORMATION pProcessInformation,
                            BOOL fWaitForThread )
{
  if ( !fWaitForThread )
    WaitForSingleObject( pProcessInformation->hProcess, INFINITE );
  else
  {
    DWORD ExitCode = 0;
    WaitForSingleObject( pProcessInformation->hThread, INFINITE );
    GetExitCodeThread( pProcessInformation->hThread, &ExitCode );
    TerminateProcess( pProcessInformation->hProcess, ExitCode );
  }
  CloseHandle( pProcessInformation->hThread );
  CloseHandle( pProcessInformation->hProcess );
  pProcessInformation->hThread = NULL;
  pProcessInformation->hProcess = NULL;
}

BTW:

Интересно, Windows 10 содержит запись по умолчанию для chrome .exe с MaxLoaderThreads, установленным в 1, чтобы отключить параллельную загрузку. Windows 10 Распределение параллельной загрузки

Возможно, для этого есть причина. ; -)

0 голосов
/ 08 января 2020

Мой вопрос заключается в том, как завершить процесс, не дожидаясь загрузки потоков

просто вызовите ExitProcess как обычно. этот вызов просто завершает процесс и, конечно, не ожидает потоков загрузчика.

Программы, которые выполняются менее чем за 30 секунд, будут зависать из-за ntdll! TppWorkerThread, ожидающий время ожидания до завершения процесса.

это неправильно или плохо сформулировано. это связано со случаем, когда процесс не вызывает ExitProcess, а просто возвращается из точки входа (или завершает сам поток).

выход из процесса, если:

  • TerminateProcess вызвано (без ошибок) - безоговорочно вызвать процесс для выхода. нет никакой очистки режима пользователя.
  • ExitProcess вызвано. однако, потому что здесь существует очистка пользовательского режима - при некоторых условиях он может зайти в тупик.
  • все потоки выходят из процесса. Ваша цитата относится именно к этому делу. если ваше «однопоточное» приложение просто выходит из точки входа без вызова ExitProcess - процесс завершается только в том случае, если в процессе нет других потоков. с параллельным загрузчиком существуют рабочие потоки, которые обычно работают до 30 секунд, если нет задач. В результате процесс завершится не сразу. но через 30 секунд. но опять же - это только если процесс не вызывает ExitProcess direct.

, в вашем случае - process ( spawn7.exe ):

  • или не вызывать ExitProcess (в этом случае он может завершиться через 30 секунд)
  • или тупик при вызове ExitProcess - в этом случае нужно посмотреть его стек вызовов и отладить его

OP говорит, что

Даже если я вызову ExitProcess (в течение 30 секунд, пока пул потоков не активен), процесс все еще там с тремя потоками из ntdll.dll, но без моего EntryPoint.

это false и не может быть. если ExitProcess вызывается - сначала, что он делает - завершает все потоки в процессе, кроме вызывающего потока. или тупик еще до этого (в вызове RtlLockHeap для кучи основного процесса). так будет:

  • или все еще существует поток "EntryPoint" в процессе
  • или только один поток в процессе (снова ваш поток "EntryPoint")

если "все еще там с тремя потоками из ntdll.dll, но не другими" - я точно могу сказать - ExitProcess не вызывается (или подключен)

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