Language agnostic 操作系统如何检测堆栈溢出?

Language agnostic 操作系统如何检测堆栈溢出?,language-agnostic,operating-system,stack-overflow,Language Agnostic,Operating System,Stack Overflow,在许多操作系统上,堆栈和堆从进程虚拟地址空间的两侧开始,并朝着另一侧增长。这允许堆栈尽可能地扩展,而不触及堆 假设我有一个导致堆栈溢出的程序。我目前的理解是,这将导致堆栈无法控制地向堆方向增长,并最终命中堆。这是正确的吗?如果是,操作系统如何检测堆栈溢出发生?操作系统似乎无法检测到程序试图使用分配给堆的虚拟内存作为堆栈的一部分,因为它们位于连续的内存区域中 我知道这是特定于操作系统的,但了解在任何操作系统中发生这种情况的机制肯定会有所帮助。这困扰了我一段时间,我似乎找不到任何好的解释。这只是直觉

在许多操作系统上,堆栈和堆从进程虚拟地址空间的两侧开始,并朝着另一侧增长。这允许堆栈尽可能地扩展,而不触及堆

假设我有一个导致堆栈溢出的程序。我目前的理解是,这将导致堆栈无法控制地向堆方向增长,并最终命中堆。这是正确的吗?如果是,操作系统如何检测堆栈溢出发生?操作系统似乎无法检测到程序试图使用分配给堆的虚拟内存作为堆栈的一部分,因为它们位于连续的内存区域中


我知道这是特定于操作系统的,但了解在任何操作系统中发生这种情况的机制肯定会有所帮助。这困扰了我一段时间,我似乎找不到任何好的解释。

这只是直觉,但确保堆栈不干扰堆听起来像是JVM的工作。我看不出有什么理由不能编写自己糟糕的编程语言,让堆栈开始覆盖堆(崩溃之前)。

操作系统为堆栈分配了一些空间。当进程访问堆栈的未分配部分时,处理器会引发页面错误,操作系统会捕获该错误。如果操作系统认为增加堆栈仍然合理,它只需为堆栈分配新的空间,并将控制权返回给进程。如果不合理,将引发堆栈溢出异常。

堆栈溢出在堆栈中向后移动——它们通过覆盖堆栈中已初始化部分的数据来工作,这正是因为堆栈向下增长才可能发生的

因此,操作系统无法检测到这种情况,因为允许进程随时修改其堆栈的初始化部分


扩展堆栈是通过进程使用堆栈空间来实现的,操作系统会被捕获到页面错误处理程序中,因为没有设置页面。一些操作系统只允许在“保护页”上进行这些访问,即当前堆栈触发重新分配之前的页面,其他操作系统则在发生故障时查看堆栈指针寄存器的内容,以确定这是否应该是堆栈访问。

大多数现代处理器都有MMU(内存管理单元),并且它是“硬件”设备,用于将虚拟地址应用于每个程序,每个程序位于其自己的内存空间中。操作系统处理此MMU,当程序要从专用内存读取或写入地址时,MMU会引发中断

这样一来,它就很难检测到这种情况。 堆栈和堆不在连续内存地址上


我还看到了使用保护字的不同方法。每个进程的堆栈都分配在堆上,并用保护区填充(类似于0xc0cac01a)通过这种方式,只需计算保护字就可以很容易地得到每个进程的堆栈大小。如果没有至少一个保护字,操作系统会引起恐慌。

@wuhounite-对于JVM来说,这是正确的,但JVM是一个模拟堆栈和堆的软件,因此在管理它时有更多的主动控制LAR,它可以设置一个堆栈深度限制。我对在C或C++语言中的本地代码发生什么感到好奇。OS不负责设置每个堆栈帧。@ CAFXX -这似乎是合理的,但是作为后续,假设我有一个堆栈框架的程序,它确实非常庞大(比如说,大于虚拟内存页面)。。难道我的程序不可能为其堆栈分配大量页面,然后“跳过”堆栈后的内存并进入堆的内存吗?操作系统对此进行了检查。当我说如果不合理,将导致堆栈溢出时,我的意思是如果堆栈增长过多(比如说,你实际上到达了堆——即使通常限制更严格且取决于平台)操作系统将通过引发堆栈溢出阻止你这样做——这通常会终止进程,因此坏事情(TM)不会发生。我突然想到,可能你在问另一种类型的堆栈溢出,即导致堆栈损坏的堆栈溢出。这是不同的,通常由流程运行时通过放置“哨兵”来检查“在堆栈中。如果sentinel被覆盖,则表示堆栈已损坏,并引发堆栈溢出/损坏异常(但通常是由进程运行时引起的,而不是由操作系统引起)。@CAFxX-我指的是操作系统级别”您的堆栈已变得过大,因此您的进程已停止运行”在这个问题上,样式溢出,但是感谢您提出关于堆栈损坏的观点。