【问题标题】:ReadFileEx, variable length - a few questionsReadFileEx,可变长度 - 几个问题
【发布时间】:2014-07-23 17:14:28
【问题描述】:

我正在尝试读取子进程的stderr。数据是使用sprintf(stderr, "some debug info\n") 创建的文本行。我正在使用带有完成例程的ReadFileEx。我不知道有多少行文本或每行有多长。那么,我应该把什么作为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;
}

【问题讨论】:

  • 澄清一下,您使用的是用CreatePipe 创建的句柄吗?
  • @RR 好点。我编辑了这篇文章,添加了一些创建管道和进程的代码部分。

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


【解决方案1】:

使用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

【讨论】:

  • 我刚刚得出这个结论,因为我对 ReadFileEx 的调用被阻塞了。我想我可能会被搞砸,直到你说要尝试我目前正在做的 CreateNamedPipe。你对读变长有什么了解吗?
  • 嗯...错过了你问题的那一部分,我更新了我的答案来解决这个问题。
  • @RR 我是否使用完成功能再次发出读取请求?我想我必须这样做,因为我的 WaitForMultipleObjects 正在等待多个子进程 - 而不是所有来自 stderr/stdout 的读取。
  • 是的,在完成例程中再次调用ReadWriteEx
  • ReadFileEx 不再阻塞。但是,我没有收到和回拨。孩子中的 sprintf(stderr...) 没有阻塞。 WriteFile(GetStdHandle(STD_OUTPUT_HANDLE)..) 在孩子中阻塞。如果我执行非重叠的 ReadFile,这不会阻止。似乎我在句柄上的重叠读取不起作用,但句柄可以写入并且可以使用非重叠 ReadFile 读取。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-17
  • 2013-08-19
  • 1970-01-01
  • 2019-11-10
相关资源
最近更新 更多