Я пытаюсь прочитать из stderr
дочернего процесса. Данные представляют собой строки текста, созданные с помощью sprintf(stderr, "some debug info\n")
. Я использую ReadFileE
x с процедурой завершения. Я не знаю, сколько строк текста или как долго каждая строка будет. Итак, что я ставлю как параметр nNumberOfBytesToRead
?
Я предполагаю, что я установил максимальный размер моего буфера, который я сделаю 4k; хотя я не знаю, если это оптимальный размер. Я предполагаю, что если строка, написанная на stderr
, короче 4k, процедура завершения не будет срабатывать. Я предполагаю, что когда 4k достигнуто, но осталось больше данных, я должен запустить другой ReadFileEx
в рамках процедуры завершения. Я знаю, что это так, потому что GetLastError
вернет ERROR_MORE_DATA
. Я надеюсь, что я получу вызов, когда буфер не заполнен, но дочерний процесс завершен. Я не уверен, что получаю обратный вызов завершения, когда дочерний процесс завершается, потому что я передал дескриптор stderr
write ребенку, когда я его создал; возможно, я получаю обратный вызов, когда я закрываю этот дескриптор. Существует ли какое - либо условие гонки, когда ребенок закрывает wrt
на мое чтении stderr
?
Вот psuedo-код того, как создаются процесс и дескрипторы:
Attr.bInheritHandle = true
CreatePipe(&hr, &hw, &Attr, 0) and SetHandleInformation(hX, HANDLE_FLAG_INHERIT) on hX the child uses.
Si.hStdXXX = handles from CreatePipe that child uses
CreateProcess(inherit=true, &Si)
Подробности (расширение Tx - это оболочка, которая вызывает ошибки):
HANDLE Create() {
STARTUPINFO SI, *pSI = NULL;
bool fInherit = m_fInherit;
if (m_fStdOut || m_fStdIn || m_fStdErr) {
fInherit = true;
SECURITY_ATTRIBUTES Attr;
Attr.nLength = sizeof(SECURITY_ATTRIBUTES);
Attr.bInheritHandle = TRUE;
Attr.lpSecurityDescriptor = NULL;
if (m_fStdOut) // Create a pipe for the child process STDOUT. The child will use the write.
CHandle::CreatePipe(m_hStdOutR, m_hStdOutW, &Attr, CP_INHERIT_WRITE);
if (m_fStdErr) // Create a pipe for the child process STDERR. The child will use the write.
CHandle::CreatePipe(m_hStdErrR, m_hStdErrW, &Attr, CP_INHERIT_WRITE);
if (m_fStdIn) // Create a pipe for the child process STDIN. The child will use the read.
CHandle::CreatePipe(m_hStdInR, m_hStdInW, &Attr, CP_INHERIT_READ);
// Set up members of the STARTUPINFO structure.
// This structure specifies the STDIN and STDOUT handles for redirection.
ZeroStruct(SI);
SI.cb = sizeof(STARTUPINFO);
SI.hStdError = m_hStdErrW, SI.hStdOutput = m_hStdOutW, SI.hStdInput = m_hStdInR;
SI.dwFlags |= STARTF_USESTDHANDLES;
pSI = &SI;
}
// m_fCpu, m_fNuma are masks to set affinity to cpus or numas
CreateProcessTx(NULL, m_szCmdLine, fInherit, m_fFlags, pSI, &m_pi, m_fCpu, m_fNuma, 5);
m_hProc = m_pi.hProcess;
m_hThread = m_pi.hThread;
if (!m_fThread)
m_hThread.Close();
return m_hProc;
}
static void CreatePipe(CHandle &hRead, CHandle &hWrite, SECURITY_ATTRIBUTES* pAttr, BYTE fInheritMask) {
HANDLE hReadTmp = NULL, hWriteTmp = NULL;
CreatePipeTx(hReadTmp, hWriteTmp, pAttr);
SetHandleInformation(hReadTmp, HANDLE_FLAG_INHERIT, (fInheritMask&CP_INHERIT_READ) ? HANDLE_FLAG_INHERIT : 0);
SetHandleInformation(hWriteTmp, HANDLE_FLAG_INHERIT, (fInheritMask&CP_INHERIT_WRITE) ? HANDLE_FLAG_INHERIT : 0);
hRead = hReadTmp;
hWrite = hWriteTmp;
}
Анонимные трубы, созданные с помощью CreatePipe
не могут использоваться асинхронно. Из документации Windows SDK:
Асинхронные (перекрывающиеся) операции чтения и записи не поддерживаются анонимными трубами. Это означает, что вы не можете использовать функции ReadFileEx и WriteFileEx с анонимными трубами. > Кроме того, параметр lpOverlapped ReadFile и WriteFile игнорируется, когда эти функции используются с анонимными трубами.
В основном CreatePipe
не принимает флаг FILE_FLAG_OVERLAPPED
, и для асинхронного ввода-вывода требуется, чтобы вы использовали флаг при создании дескриптора файла.
Вам нужно будет использовать CreateNamedPipe
для создания именованных каналов. В вопросе Overlapped I/O на анонимном канале есть ответ со ссылкой на заменяемую функцию MyCreatePipeEx
вы можете использовать.
Ваш порт завершения должен получить событие чтения с нулевой длиной после попытки чтения из канала, который был закрыт на другом конце.
Чтобы прочитать переменное количество данных из клиентского процесса, просто выдайте запросы на чтение любого размера, который вы найдете удобным, и будьте готовы обработать события чтения, которые короче, чем вы просили. Не интерпретируйте короткую, но ненулевую длину как EOF. Продолжайте выдавать запросы на чтение до получения нулевой длины или ошибки.
Также WaitForMultipleObjects
не будет работать с процедурами завершения, поскольку они вызываются только тогда, когда поток находится в переменном состоянии. Используйте WaitForMultipleObjectEx
с аргументом bAlertable
установленным в true. Эта функция вернет WAIT_IO_COMPLETION
после выполнения одной или нескольких процедур завершения. В этом случае вы, вероятно, захотите снова вызвать WaitForMultipleObjectEx
снова.
CreatePipe
?