我很想知道堆栈和堆碰撞时会发生什么。如果有人遇到过这种情况,请他们解释一下这个场景。
6 回答
在现代操作系统上运行的现代语言中,当您尝试增加堆时,您将获得堆栈溢出(万岁!)或失败malloc()
。但并不是所有的软件都是现代的,所以让我们看看故障模式:sbrk()
mmap()
如果堆栈增长到堆中,典型的 C 编译器将默默地开始覆盖堆的数据结构。在现代操作系统上,会有一个或多个虚拟内存保护页来防止堆栈无限增长。只要保护页面中的内存量至少与增长过程的激活记录的大小一样大,操作系统就会向您保证段错误。如果您在没有 MMU 的机器上运行 DOS,那么您可能已经被水洗了。
如果堆增长到堆栈中,操作系统应该始终意识到这种情况,并且某种系统调用将会失败。执行
malloc()
几乎肯定会注意到失败并返回NULL
。之后会发生什么取决于你。
我总是对编译器编写者希望操作系统放置保护页以防止堆栈溢出的意愿感到惊讶。当然,这个技巧很有效,直到你开始拥有数千个线程,每个线程都有自己的堆栈......
这将取决于平台。在许多平台上,它实际上根本不会发生(堆和堆栈分配在不同的页面中,两者都不会相遇。
请记住,堆向上增长和堆栈向下增长的想法只是概念性的。在非常小的系统(如运行 CP/M 的旧 8 位微控制器)和一些 PIC 和其他平面内存模型系统(没有 MMU 或任何其他虚拟或受保护内存支持的系统)上,堆和堆栈实际上可能是以这种方式实施。在那种情况下,行为将是未定义的......但几乎可以肯定,只要代码试图返回损坏堆栈顶部的某个地址或跟随从堆的一部分到另一部分的间接指针或 . ..
无论如何,您不会在任何现代通用工作站或服务器上看到它。您将达到资源限制并出现 malloc 故障,或者您将遇到虚拟内存,最终系统将自己陷入颤抖的“点击红色开关”中。
在这样的时代,是时候转向埃贡·斯宾格勒博士的圣言了……
- Egon Spengler 博士:有件非常重要的事情我忘了告诉你。
- 彼得文克曼博士:什么?
- Egon Spengler 博士:不要让堆与堆栈发生冲突。
- Peter Venkman 博士:为什么?
- Egon Spengler 博士:那会很糟糕。
- Peter Venkman 博士:我对这里的“好/坏”这件事有点模糊。你是什么意思,“坏”?
- Egon Spengler 博士:试着想象一下,你所知道的所有生命都会瞬间停止,你体内的每个分子都以光速爆炸。
- Ray Stantz 博士:质子完全逆转!
- Peter Venkman 博士:那很糟糕。好的。好的,重要的安全提示。谢谢,埃贡。
如果幸运的话,您会遇到内存不足异常或堆栈异常。如果你不走运,程序会进入无效内存并引发错误的内存异常。如果您非常不走运,程序会继续运行并丢弃不应该的东西,您永远不知道程序失败的原因。
最后当然宇宙可能会破裂。
如果发生堆栈/堆溢出,您将收到分段错误或内存分配错误。这是一个例子:
void recursiveFun ()
{
static int i;
// char *str= (char *)malloc (100);
printf ("%d\t", i++);
recursiveFun ();
// free (str);
}
假设,你调用上面的函数,它会用完堆栈,程序会崩溃。现在,删除注释行并再次调用该函数,您会发现分段错误发生的时间比早期版本少,递归也少。[在我的测试环境中,堆栈溢出发生在第一种情况下 5237765 递归之后,而在第二种情况下,它发生在 2616325 递归之后。]
它会给出堆栈溢出错误。否则它将使新的堆内存分配函数(如 malloc())失败。