Как правильно прочитать вывод stdout / stderr дочернего процесса? - PullRequest
2 голосов
/ 02 июля 2010

Я написал программу a.exe, которая запускает другую написанную мной программу, b.exe, используя функцию CreateProcess .Вызывающая сторона создает два канала и передает концы записи обоих каналов в CreateProcess как дескриптор stdout / stderr для использования в дочернем процессе.Это практически то же самое, что и образец Создание дочернего процесса с перенаправленным вводом и выводом на MSDN.

Поскольку, похоже, он не может использовать один вызов синхронизации, который ожидаетчтобы процесс мог выйти из или данных на stdout или stderr (функция WaitForMultipleObjects не работает на каналах), у вызывающей стороны есть два работающих потока, которые оба выполняют (блокирование) ReadFile вызывает конец чтения каналов stdout / stderr;вот точный код «процедуры чтения потока», которая используется для stdout / stderr (я сам не писал этот код, я полагаю, что это сделал какой-то коллега):

DWORD __stdcall ReadDataProc( void *handle )
{
    char buf[ 1024 ];
    DWORD nread;
    while ( ReadFile( (HANDLE)handle, buf, sizeof( buf ), &nread, NULL ) &&
            GetLastError() != ERROR_BROKEN_PIPE ) {
        if ( nread > 0 ) {
            fwrite( buf, nread, 1, stdout );
        }
    }
    fflush( stdout );
    return 0;
}

a.exe затем используетпростой вызов WaitForSingleObject для ожидания завершения b.exe.Как только этот вызов возвращается, два потока чтения завершаются (потому что каналы разорваны), и концы чтения обоих каналов закрываются с помощью CloseHandle .

Теперь проблема, с которой я столкнулся, заключается в следующем:b.exe может (в зависимости от ввода пользователя) запускать внешние процессы, которые живут дольше самого b.exe, в основном процессы демона.В этом случае происходит то, что концы записи каналов stdout / stderr наследуются этому процессу демона, поэтому канал никогда не прерывается.Это означает, что вызов WaitForSingleObject в a.exe возвращает (потому что b.exe завершен), но вызов CloseHandle для любого из блоков каналов, потому что оба потока чтения все еще находятся в своем (блокирующем!) Вызове ReadFile.

Как решить эту проблему, не прерывая обе темы чтения с помощью грубой силы ( TerminateThread ) после возврата b.exe?Если возможно, я бы хотел избежать любых решений, которые включают опрос труб и / или процесс тоже.

ОБНОВЛЕНИЕ: Вот что я попробовал до сих пор:

  1. Не имеющий b.exe наследования a.exe;это не работаетMSDN, в частности, говорит, что дескрипторы, передаваемые в CreateProcess, должны быть наследуемыми.
  2. Очистка наследуемого флага в stdout / stderr внутри b.exe: похоже, не имеет никакого эффекта (меня это удивило бы, если бы это произошло).
  3. Имея процедуру ReadDataProc (которая читает на обоих каналах), проверьте, действительно ли работает b.exe в дополнение к проверке ERROR_BROKEN_PIPE.Конечно, это не сработало (но я понял это позже), потому что поток заблокирован в вызове ReadFile.

Ответы [ 4 ]

2 голосов
/ 03 июля 2010
  1. Использовать именованный канал и асинхронный файл ReadFile
    или
  2. Синтаксический анализ результата чтения канала и поиск конца (в вашем случае это может быть слишком сложно)
1 голос
/ 04 июля 2010

Что происходит в этом случае, так это то, что написание концов stdout / stderr трубы наследуются этому демону процесс, поэтому труба никогда не сломана.

Демоны должны закрыть свои унаследованные файловые дескрипторы.

0 голосов
/ 19 августа 2013

установить глобальный флаг (bool exit_flag) и записать что-либо в pipe в a.exe

0 голосов
/ 21 июля 2010

Похоже, что в версиях Windows до Windows Vista (где вы можете использовать функцию CancelSynchronousIO , нет никакого способа прекратить чтение потоков, используя TerminateThread .

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

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