我想知道我是否可以在MinGW开发链上使用poll()函数。我有CodeBlocks + MinGW。非常感谢。
答案 0 :(得分:2)
最后,听说mingw不支持poll()或功能有限。
那是在2009年。
在2020年...受支持,但仍然存在缺陷,如Git 2.25.2(2020年3月)所示,其中对MinGW的poll()
仿真进行了改进。
请参见commit 94f4d01的Alexandr Miloslavskiy (SyntevoAlex
)(2020年2月17日)。
(由Junio C Hamano -- gitster
--在commit 1ac37de中合并,2020年3月9日)
mingw
:发送STDIN时挂起的解决方法签名人:Alexandr Miloslavskiy
说明
这里的问题是
poll()
的实现存在缺陷。
当尝试查看是否可以不阻塞地编写管道时,它最终调用NtQueryInformationFile()
并测试WriteQuotaAvailable
。但是,配额的含义被误解了。
当某些数据写入管道,或在管道上有待处理的读取时,配额的值会减小。
因此,如果有一个待读的读取大小大于管道缓冲区的大小,则poll()
会认为该管道不可写,并且将永远挂起,这通常意味着两个管道用户都将死锁。我研究了问题,发现Windows管道跟踪两个值:
QuotaUsed
和BytesInQueue
。
poll()
中的代码显然想知道BytesInQueue
而不是配额。
不幸的是,BytesInQueue
仅可从管道的读取端请求,而poll()
则可接收写入端。
poll()
的git实现是从gnulib复制而来的,到目前为止它还包含一个有缺陷的实现。我也了解了cygwin中的实现,该实现也以微妙的方式被破坏了。它在
pipe_data_available()
中使用以下代码:fpli.WriteQuotaAvailable = (fpli.OutboundQuota - fpli.ReadDataAvailable)
但是,
ReadDataAvailable
总是在管道的写入端返回0,从而将代码转换为返回管道总缓冲区大小的混淆版本,我想这反过来会使poll()
总是说管道可写。
引入代码的提交并没有说明此更改,因此可能只是一些调试代码。这些是git中使用的典型大小:
- 0x2000-默认读取大小为
strbuf_read()
- 0x1000-CRT中的默认读取大小,由
使用strbuf_getwholeline()
- 0x2000-
compat\mingw.c
中的管道缓冲区大小结果,一旦子进程使用
strbuf_read()
,父进程中的poll()
将永远挂起,从而使两个进程都死锁。这会导致两个可观察到的行为:
- 如果父进程开始快速发送STDIN(通常是这种情况),则第一个
poll()
将成功,第一个块将通过。
MAX_IO_SIZE_DEFAULT
是8MB,因此,如果STDIN超过8MB,它将死锁。- 如果父进程出于某种原因(包括操作系统调度程序)稍等,而子进程首先发出
strbuf_read()
,则即使在小型STDIN上,子进程也会立即死锁。该问题由
git stash push
说明,该问题当前会将整个补丁读取到内存中,然后通过STDIN发送到git apply
。
如果补丁超过8MB,则git会在Windows上挂起。可能的解决方案
- 以某种方式获得
BytesInQueue
而不是QuotaUsed
我进行了彻底的搜索,没有找到从管道的写入端获取值的任何方法。
。- 还要将管道的读取端交给
poll()
可以这样做,但是可能会邀请一些脏代码,因为poll()
- 可以一次接受多个管道
- 可以接受不是管道的东西
- 应该有众所周知的签名。
。- 使
poll()
对于管道的写入结束始终回复“可写”
毕竟,似乎cygwin(偶然地?)已经做了很多年了。
另外,应注意,pump_io_round()
写入8MB的块,完全忽略了管道的缓冲区大小仅为8KB的事实,这意味着管道在该一次写入过程中被阻塞了很多次。
如果孩子的STDERR / STDOUT在尝试处理8MB STDIN时被阻塞,则可能会导致死锁。
这样的死锁可以通过以下方式克服:每轮写入少于管道缓冲区的大小,并始终在开始下一轮之前从STDOUT / STDERR读取所有内容。
因此,使poll()
始终答复“可写”不应引起任何新问题或阻止任何将来的解决方案。
。- 增加管道缓冲区的大小
BytesInQueue
和QuotaUsed
之间的区别在于待处理读取的大小。因此,如果缓冲区大于读取的大小,poll()
将不会轻易挂起。但是,我发现例如strbuf_read()
会在读取大量输入时变得越来越饥饿,最终超过任何合理的管道缓冲区大小。选择的解决方案
使
poll()
对于管道的写入结束始终返回“可写”。
希望有一天,有人会找到一种正确实施它的方法。复制
printf "%8388608s" X >large_file.txt git stash push --include-untracked -- large_file.txt
为了避免降低测试套件的速度,我决定不将其作为测试。
我不希望再遇到特定的问题,git stash push
可能会被重新设计以避免通过STDIN发送整个补丁。
答案 1 :(得分:1)
最后我听说,poll()
要么不支持,要么在mingw上提供有限的功能。但是,您可以提供自己内部使用poll()
的基本select()
。
答案 2 :(得分:1)
Windows还提供WSAPoll()
功能,但性能比select()
差,从MSDN博客开始,它只是实现了改进的可移植性:
http://blogs.msdn.com/b/wndp/archive/2006/10/26/wsapoll.aspx