技术细节
0 [main] us 0 init_cheap: VirtualAlloc pointer is null, Win32 error 487
AllocationBase 0x0, BaseAddress 0x68570000, RegionSize 0x2A0000, State 0x10000
PortableGit\bin\bash.exe: *** Couldn't reserve space for cygwin's heap, Win32 error 0
这种症状本身与可执行文件的映像库、损坏的 Cygwin 共享内存部分、冲突的 DLL 版本等无关。
Cygwin 代码未能在这个固定地址 0x68570000 处为其堆分配约 5 MB 的大内存块,而那里显然只有一个约 2.5 MB 的洞。相关代码见msysgit 源码。
为什么地址空间的那部分不是空闲的?
可能有很多原因。在我的情况下,它是在冲突地址加载的一些其他模块:
最后一个地址大约是 0x68570000 + 5 MB = 0x68C50000,但是这些 WOW64 相关的 DLL 从 0x68810000 向上加载,这会阻塞分配。
每当有一些共享 DLL 时,Windows 通常会尝试在所有进程中将其加载到相同的虚拟地址,以节省一些重定位处理。这次这些系统组件以某种方式加载到一个冲突的地址只是运气不好。
为什么你的 Git 中有 Cygwin?
因为 Git 是一个丰富的套件,由一些低级命令和许多有用的实用程序组成,并且主要在类 Unix 系统上开发。为了能够在不进行大量重写的情况下构建和运行它,它至少需要部分类 Unix 环境。
为了实现这一点,人们发明了 MinGW 和 MSYS——一套最小的构建工具,用于在 Windows 上以类 Unix 的方式开发程序。MSYS 还包含一个共享库 this msys-1.0.dll
,它有助于解决运行时两个平台之间的一些兼容性问题。其中许多部分来自 Cygwin,因为已经有人必须在那里解决相同的问题。
所以不是 Cygwin,而是 MinGW 的运行时 DLL,这里的行为很奇怪。
在 Cygwin 中,与 MSYS 1.0 中的代码相比,这段代码实际上发生了很大变化——该文件的最后一条提交消息显示“Import Cygwin 1.3.4”,这是从 2001 年开始的!
当前的 Cygwin和MSYS 的新版本- MSYS2 - 已经有不同的逻辑,希望更健壮。只有旧版本的 Git for Windows 仍然使用旧的损坏的 MSYS 系统构建。
清洁解决方案:
- 安装适用于 Windows 2 的 Git - 它是使用新的、正确维护的 MSYS2 构建的,并且还具有许多新功能、大量错误修复、安全性改进等。如果可能,还建议使用 64 位版本。但是对于 32 位系统, rebase 变通方法是在幕后自动执行的,因此发生问题的可能性也应该更低。
- 简单地重新启动计算机以清理地址空间(将这些模块加载到不同的随机地址)可能会起作用,但实际上,只需升级到 Windows 2 的 Git 以获取安全修复程序(如果没有其他问题)。
哈克解决方案:
- 更改
PATH
有时会起作用,因为msys-1.0.dll
在不同版本的 Git 或其他基于 MSYS 的应用程序中可能存在不同版本,它们可能使用不同的地址、不同大小的堆等。
- 变基
msys-1.0.dll
可能会浪费时间,因为 1) 作为 DLL,它已经具有重定位信息,并且 2)“在任何版本的 Windows 操作系统中,都不能保证 (...) DLL 将始终加载到相同的地址空间”无论如何(来源)。这可以提供帮助的唯一方法是,如果它msys-1.0.dll
本身加载到它然后尝试使用的冲突地址。显然有时会出现这种情况,因为这就是 Git for Windows 人员在32 位系统上自动执行的操作。
- 考虑到上述发现,我最初对二进制文件进行了修补
msys-1.0.dll
以使用不同的值,_cygheap_start
并立即解决了问题。