ReadFileEx,可变长度 - 几个问题

时间:2014-07-23 17:14:29

标签: c++ windows file-io 64-bit overlapped-io

我正在尝试从stderr读取子进程。数据是使用sprintf(stderr, "some debug info\n")创建的文本行。我正在使用ReadFileE x和完成例程。我不知道有多少行文本或每行多长。那么,我将什么作为nNumberOfBytesToRead参数?

我的猜测是我把缓冲区的最大尺寸设为4k;虽然我不知道这是否是最佳尺寸。我猜测如果写入stderr的行短于4k,则完成例程将不会触发。我猜测当达到4k但仍有更多数据时,我必须在完成例程中触发另一个ReadFileEx。我知道情况就是这样,因为GetLastError会返回ERROR_MORE_DATA。我希望当缓冲区未满时我接到一个电话,但子进程已经退出。我不确定在子进程退出时我得到完成回调,因为我在创建它时将stderr写入句柄传递给了孩子;也许我在 I 关闭那个句柄时得到了回调。当孩子关闭wrt到我的阅读stderr时,是否有任何竞争条件?

以下是创建进程和句柄的伪代码:

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;
}

1 个答案:

答案 0 :(得分:2)

使用CreatePipe创建的匿名管道无法异步使用。从Windows SDK文档:

  

匿名管道不支持异步(重叠)读写操作。   这意味着您不能将ReadFileEx和WriteFileEx函数与匿名管道一起使用。 >此外,当这些>函数与匿名管道一起使用时,将忽略ReadFile和WriteFile的lpOverlapped参数。

基本上CreatePipe不接受FILE_FLAG_OVERLAPPED标志,异步I / O要求您在创建文件句柄时使用该标志。

您必须使用CreateNamedPipe来创建命名管道。问题Overlapped I/O on anonymous pipe已回答了您可以使用的替换函数MyCreatePipeEx的链接。

尝试从另一端已关闭的管道读取后,您的完成端口应收到零长度读取事件。

要从客户端进程读取可变数量的数据,只需发出您认为方便的任何大小的读取请求,并准备好处理比您请求的更短的读取事件。不要将短而非零的长度解释为EOF。继续发出读取请求,直到读取零长度或错误。

同样WaitForMultipleObjects不能使用完成例程,因为它们仅在线程位于alterable state时才被调用。使用WaitForMultipleObjectEx参数设置为bAlertable为true。运行一个或多个完成例程后,此函数将返回WAIT_IO_COMPLETION。在这种情况下,您可能希望立即再次致电WaitForMultipleObjectEx