Windows中的命名管道,FILE_FLAG_OVERLAPPED和PIPE_NOWAIT之间的区别

Posted

技术标签:

【中文标题】Windows中的命名管道,FILE_FLAG_OVERLAPPED和PIPE_NOWAIT之间的区别【英文标题】:Named pipe in windows, difference between FILE_FLAG_OVERLAPPED and PIPE_NOWAIT 【发布时间】:2018-11-10 09:46:52 【问题描述】:

我在windows中使用命名管道,对FILE_FLAG_OVERLAPPEDPIPE_NOWAIT之间的区别感到困惑,这是CreateNamedPipe中设置的参数,我这样设置参数。

HANDLE hPipe = CreateNamedPipe(
    lpszPipename,             // pipe name 
    PIPE_ACCESS_DUPLEX |      // read/write access 
    FILE_FLAG_OVERLAPPED,     // overlapped mode 
    PIPE_TYPE_MESSAGE |       // message-type pipe 
    PIPE_READMODE_MESSAGE |   // message read mode 
    PIPE_WAIT,                // blocking mode 
    PIPE_UNLIMITED_INSTANCES, // unlimited instances 
    BUFSIZE * sizeof(TCHAR),    // output buffer size 
    BUFSIZE * sizeof(TCHAR),    // input buffer size 
    PIPE_TIMEOUT,             // client time-out 
    NULL);                    // default security attributes

ConnectNamedPipe 立即返回,我从GetLastError 得到ERROR_IO_PENDING。使用非阻塞等待句柄,连接操作立即返回零,GetLastError 函数返回ERROR_IO_PENDING。但是 MSDN 告诉: 使用非阻塞等待句柄,连接操作立即返回零,GetLastError 函数返回ERROR_PIPE_LISTENING 那么,nonblocking-wait 是什么意思,PIPE_NOWAITFILE_FLAG_OVERLAPPED,非常感谢!

【问题讨论】:

似乎MSDN在使用PIPE_ NOWAIT时使用术语“非阻塞模式”,在使用@987654334时使用术语“重叠模式” @。 PIPE_NOWAIT"supported for compatibility with Microsoft LAN Manager version 2.0 and should not be used to achieve asynchronous I/O" 非常不同的选项。 FILE_FLAG_OVERLAPPED 设置套接字以便它可以处理异步 I/O,您可以使用 OVERLAPPED 参数调用 ReadFile() 并稍后处理读取完成。管道很常见,除非流量非常高,否则您想避免过多地阻塞线程。 PIPE_NOWAIT 对于同步 I/O 很有用,它可以防止 ReadFile() 在没有数据的情况下阻塞。然后需要反复调用 ReadFile(),以通用术语进行轮询。看看像 boost::asio 这样的库来处理管道。 【参考方案1】:

PIPE_NOWAIT 表示在句柄上启用了非阻塞模式。在这种模式下,ReadFileWriteFileConnectNamedPipe 总是立即完成

FILE_FLAG_OVERLAPPED 表示在句柄上启用了异步模式。如果启用此模式,所有非同步 io [1] 操作总是立即返回

所以FILE_FLAG_OVERLAPPED vs PIPE_NOWAIT - 这是立即返回 vs 完成立即。

completed立即(包括return立即)表示api返回时io操作已经完成。但反之亦然。如果操作立即返回,这并不意味着操作已经完成。如果操作仍未完成 ntapi 返回码STATUS_PENDING。 win32 api 在这种情况下通常将最后一个错误设置为ERROR_IO_PENDING

在异步句柄模式下,存在 3 种方式确定 io 操作何时完成。

    将句柄绑定到IOCP(通过CreateIoCompletionPortBindIoCompletionCallbackCreateThreadpoolIo)。结果当 io 完成 - 指向 OVERLAPPED 的指针,我们将其传递给 io 调用 - 将排队返回 IOCP(如果是 BindIoCompletionCallbackCreateThreadpoolIo 系统自己创建 IOCP 并监听它 并调用我们注册的回调,当指向OVERLAPPED 的指针将 排队到IOCP) 一些win32 api如ReadFileExWriteFileEx和所有ntapi让 指定将在上下文中调用的 APC 完成例程 io 操作完成时开始 io 操作的线程。 在这种情况下,线程必须执行警报等待。这个等待不是 与 IOCP 的绑定句柄兼容(我们不能在 如果文件句柄绑定到 IOCP,则 api 调用 - 系统返回无效 参数错误) 我们可以创建事件并将其传递给 api 调用(通过 OVERLAPPED::hEvent) - 在这种情况下,此事件将被重置 io 操作开始时的系统,并在 io 时设置为信号状态 操作完成。在这种情况下,与前 2 个选项不同,我们有 io 时没有额外的上下文(在指向OVERLAPPED 的指针中) 操作完成。通常这是最糟糕的选择。

[1] 存在一些始终是同步 api 的 io 操作。例如GetFileInformationByHandleExSetFileInformationByHandle。但几乎 io 操作不是同步 io。所有这些 io 操作都将指向 OVERLAPPED 的指针作为参数。因此,如果 api 签名中没有指向 OVERLAPPED 的指针 - 这是同步 api 调用。如果存在 - 通常是异步的(例如 CancelIoEx 异常,其中指向重叠的指针与当前操作无关,而是与我们想要取消的先前 io 操作相关)。特别是ReadFileWriteFileDeviceIoControlConnectNamedPipe(内部这是调用DeviceIoControlFSCTL_PIPE_LISTEN))不是同步io api

【讨论】:

以上是关于Windows中的命名管道,FILE_FLAG_OVERLAPPED和PIPE_NOWAIT之间的区别的主要内容,如果未能解决你的问题,请参考以下文章

适用于 Windows 和 Linux 的 Go 中的命名管道

无法在 Windows 7 中的命名管道内创建进程

Windows 中的 python 2 和 python 3 之间的命名管道的工作方式有啥不同吗?

从底层的角度来看,Windows 中的命名管道和远程过程调用 (RPC) 有啥区别?

使用 App.Config 在 Windows 服务中的 WCF 命名管道

Windows中的命名管道,FILE_FLAG_OVERLAPPED和PIPE_NOWAIT之间的区别