使用重叠/异步I/O读取命名管道

3
我有一段使用命名管道进行消息模式(PIPE_TYPE_MESSAGE)和带有重叠I/O(FILE_FLAG_OVERLAPPED)的服务的真正老代码。
该代码执行以下操作:
1. 使用带有重叠I/O(头部+消息长度)的ReadFile读取4个字节。客户端使用WriteFile调用编写了此命令。 2. 读取完4个字节后,调用ReadFile以已知长度读取其余消息,而不指定OVERLAPPED结构。 3. 命令执行后,例程继续在第1阶段,并等待下一个命令。
当我阅读文档时,发现重叠操作需要使用设置了FILE_FLAG_OVERLAPPED标志的文件、命名管道或通信设备。当线程调用函数(如ReadFile函数)执行重叠操作时,调用线程必须指定指向OVERLAPPED结构的指针。(如果此指针为NULL,则函数返回值可能会错误地指示操作已完成。)
因此,我必须假设这段代码将无法工作或者至少是错误的...
实际上,这段代码已经15年了,在数百台机器上运行良好,没有问题。
那么我是否需要告诉我的老板和同事,这段代码存在缺陷,它只是幸运地工作,需要进行更正?

2
请参阅https://devblogs.microsoft.com/oldnewthing/20120411-00/?p=7883,“我知道重叠文件句柄需要lpOverlapped,但如果我省略它,为什么有时候会起作用?” - Solomon Ucko
2个回答

3

是的,这段代码不正确但可以正常工作而没有错误。调用ReadFile时会调用ZwReadFile。它的第五个参数-IoStatusBlock-指向一个IO_STATUS_BLOCK结构的指针-是强制性的,并且对于任何文件和任何I/O类型,它始终必须不为0。因此,在调用ZwReadFile时,ReadFile必须传递指向IO_STATUS_BLOCK的指针。如果您将OVERLAPPED的指针设置为非0值,则它会将该指针作为IO_STATUS_BLOCK传递给ZwReadFileOVERLAPPED的前两个成员对应于IO_STATUS_BLOCK。但是,如果您将OVERLAPPED的指针设置为0,则ReadFile会分配本地的IO_STATUS_BLOCK iosb变量并将其传递给ZwReadFileIO_STATUS_BLOCK内存必须在I/O未完成之前有效。因为在I/O结束时,内核会将最终状态写入此内存。从另一方面来说-如果您使用局部变量作为IO_STATUS_BLOCK,则在ReadFile返回后它将变得无效(指向堆栈中的任意内存)。在同步I/O的情况下-这里没有问题,因为ReadFile不会返回直到I/O完成。但是,在异步I/O的情况下-这将是未定义行为-ReadFile可能在I/O仍在进行中时返回。因此,在I/O结束之前,IO_STATUS_BLOCK将变得无效。当I/O实际上结束时-将在线程堆栈的任意位置覆盖内存。这可能没有任何影响,也可能破坏您的堆栈。取决于您此时所在的位置(堆栈指针值)。


2
阅读更多文档后,我必须得出结论:是的,该代码可以说是不正确的。特别是这些关于`ReadFile`的文档:
“如果使用`FILE_FLAG_OVERLAPPED`打开了`hFile`参数,则需要指向`OVERLAPPED`结构的指针,否则可以为NULL。”
“如果使用`FILE_FLAG_OVERLAPPED`打开`hFile`,则`lpOverlapped`参数必须指向一个有效且唯一的`OVERLAPPED`结构,否则该函数可能错误地报告读取操作已完成。”
请注意,微软文档会随着时间的推移而更新,且在编写代码时,文档可能不够清晰或不完整。
它可能正常工作是因为管道处于消息模式。使用长度前缀头与消息模式管道一起使用很奇怪,因为消息模式会自动处理消息边界。在这种特定情况下,整个消息已经在本地操作系统中,因此不正确的同步读取将始终成功。
实际上,该协议(带有单独的头)似乎是设计用于字节流抽象,例如字节模式管道或TCP/IP连接。该协议最初可能是为字节模式管道设计的,当它无法正常工作时,再切换到消息模式管道(由于消息可能尚未完全存在,因此同步的`ReadFile`在字节模式管道上的行为可能会出现意外情况)。

1
我可以追踪TFS中自2009年以来的所有代码更改。在此日期之前,代码存储在Visual Source Safe中,我没有旧代码更改的备份。这似乎是一个合理的解释。但实际上,即使在消息模式下:如果发送方写了两条消息(WriteFile,WriteFile),并且接收方使用大缓冲区调用ReadFile一次,那么如果它们适合读取缓冲区,接收方将会接收到2条消息吗?我从未检查过... - xMRi
@xMRi:我相信这将是不同的读取(假设使用了PIPE_READMODE_MESSAGE)。虽然我不是100%确定,因为我没有测试过,而且自从我使用命名管道以来已经过去了... 20年?... - Stephen Cleary
管道模式这里是绝对无关的。 - RbMm

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接