我已经实现了PARLANSE,这是一种 MS Windows 下的语言,它使用仙人掌堆栈来实现并行程序。堆栈块是在每个函数的基础上分配的,并且大小正好适合处理局部变量、表达式临时推送/弹出和库调用(包括库例程工作的堆栈空间)。这种堆栈帧实际上可以小到 32 字节,而且通常如此。
这一切都很好,除非代码做了一些愚蠢的事情并导致硬件陷阱......此时Windows似乎坚持将整个x86机器上下文“推入堆栈”。如果包含 FP/MMX/等,这大约是 500+ 字节。注册,它确实如此。自然地,对 32 字节堆栈的 500 字节推送会破坏它不应该的东西。(硬件在陷阱上推了几句话,但不是整个上下文)。
[编辑 2012 年 11 月 27 日:有关Windows 实际推送的可笑堆栈数量的测量详细信息,请参阅此内容]
我可以让 Windows 将异常上下文块存储在其他地方(例如,存储到特定于线程的位置)吗?然后,软件可以处理线程上的异常并处理它,而不会溢出我的小堆栈帧。
我不认为这是可能的,但我想我会问更多的观众。是否有可能导致这种情况发生的操作系统标准调用/接口?
如果我可以让 MS 让我的进程有选择地定义一个上下文存储位置“contextp”,那么在操作系统中做这件事将是微不足道的,默认情况下,该位置已初始化为启用当前的遗留行为。然后替换中断/陷阱向量代码:
hardwareint: push context
mov contextp, esp
... 和 ...
hardwareint: mov <somereg> contextp
test <somereg>
jnz $2
push context
mov contextp, esp
jmp $1
$2: store context @ somereg
$1: equ *
具有保存 somereg 等所需的明显更改。
[我现在要做的是:检查每个函数的生成代码。如果它有机会生成陷阱(例如,除以零),或者我们正在调试(可能是错误的指针 deref 等),请为 FP 上下文的堆栈帧添加足够的空间。堆栈帧现在最终大小为 ~~ 500-1000 字节,程序不能递归到那么远,这对于我们正在编写的应用程序来说有时是一个真正的问题。所以我们有一个可行的解决方案,但它使调试变得复杂]
编辑 8 月 25 日:我设法将这个故事传达给了一位微软内部工程师,他显然有权找出 MS 中谁可能真正关心。解决的希望渺茫。
编辑 9 月 14 日:MS Kernal Group Architect 听说了这个故事并且很同情。他说 MS 将考虑一种解决方案(如提议的解决方案),但不太可能包含在服务包中。可能需要等待下一个版本的 Windows。(叹息……我可能会变老……)
编辑:2010 年 9 月 13 日(1 年后)。微软没有采取任何行动。我最近的噩梦:在 Windows X64 上运行 32 位进程的陷阱是否会在中断处理程序假装推送 32 位上下文之前将整个 X64 上下文推送到堆栈上?那会更大(整数寄存器的两倍宽,SSE 寄存器的两倍(?))?
编辑:2012 年 2 月 25 日:(1.5 年过去了……)微软方面没有任何反应。我想他们只是不关心我的并行性。我认为这是对社区的伤害;MS 在正常情况下使用的“大堆栈模型”限制了通过吃大量 VM 可以在任何时刻活着的并行计算量。PARLANSE 模型将让一个应用程序拥有一百万个处于运行/等待状态的实时“颗粒”;这确实发生在我们的一些应用程序中,其中“并行”处理了 1 亿个节点图。PARLANSE 方案可以使用大约 1Gb 的 RAM 来做到这一点,这非常易于管理。如果您尝试使用 MS 1Mb“大堆栈”,您将需要 10^12 字节的 VM 仅用于堆栈空间,而我
编辑:2014 年 4 月 29 日:(4 年过去了)。 我猜MS只是不读SO。 我在 PARLANSE 上做了足够多的工程,所以我们只在调试期间或进行 FP 操作时支付大堆栈帧的代价,所以我们设法找到了非常实用的方法来解决这个问题。MS继续令人失望;不同版本的 Windows 推送到堆栈上的东西的数量似乎有很大差异,而且超出了对硬件上下文的需求。有一些迹象表明,这种可变性的某些原因是由于非 MS 产品(例如防病毒)在异常处理链中卡住了它们的鼻子。为什么他们不能从我的地址空间之外做到这一点?无论如何,我们通过简单地为 FP/调试陷阱添加一个大的溢出因子来处理所有这些,并等待现场中不可避免的 MS 系统超过该数量。