Я пытаюсь читать с stderr
детского процесса. Данные представляют собой строки текста, созданные с sprintf(stderr, "some debug info\n")
, я использую ReadFileE
х с рутиной завершения. Я не знаю, сколько строк текста или сколько будет каждая строка. Итак, что я ставлю как nNumberOfBytesToRead
параметр?
Я предполагаю, что я поставил максимальный размер буфера, который я сделаю 4k; хотя я не знаю, если это оптимальный размер. Я предполагаю, что если строка написана stderr
короче 4к, подпрограмма завершения не сработает. Я предполагаю, что когда 4k достигнут, но больше данных остается, я должен запустить другой ReadFileEx
в рамках процедуры завершения. Я буду знать, что это так, потому что GetLastError
вернусь ERROR_MORE_DATA
, Я надеюсь, что мне вызовут, когда буфер не заполнен, но дочерний процесс завершен. Я не уверен, что получаю обратный вызов при завершении дочернего процесса, потому что я передал stderr
написать дескриптор ребенку, когда я его создал; может быть, я получу обратный звонок, когда я закройте эту ручку. Есть ли какие-либо условия гонки, когда ребенок закрывается 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's 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's 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's 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
создавать именованные каналы. Вопрос Перекрытый ввод / вывод на анонимном канале ответ со ссылкой на функцию замены MyCreatePipeEx
ты можешь использовать.
Ваш порт завершения должен получить событие чтения нулевой длины после попытки чтения из канала, который был закрыт на другом конце.
Чтобы прочитать переменное количество данных из клиентского процесса, просто отправьте запросы на чтение любого размера, который вам удобен, и будьте готовы обрабатывать события чтения, которые короче, чем вы запрашивали. Не интерпретируйте короткую, но ненулевую длину как EOF. Продолжайте выдавать запросы на чтение, пока не получите чтение нулевой длины или ошибку.
Также WaitForMultipleObjects
не будет работать с подпрограммами завершения, так как они вызываются только тогда, когда поток находится в изменяемое состояние. использование WaitForMultipleObjectEx
с bAlertable
аргумент установлен в значение true. Эта функция вернет WAIT_IO_COMPLETION
после запуска одной или нескольких процедур завершения. В этом случае вы, вероятно, захотите немедленно позвонить WaitForMultipleObjectEx
снова.