运行时环境可以检测到无限循环吗?


19

运行时环境是否有可能检测到无限循环并随后停止相关的进程,或者实现这样的逻辑是否等效于解决暂停问题?

出于这个问题的目的,我定义了一个“无限循环”来表示一系列指令和相关的起始堆栈/堆数据,这些数据在执行时将过程返回到与之前完全相同的状态(包括数据)启动无限循环。(换句话说,生成pi的无限长十进制扩展的程序不会“卡在”“无限循环”中,因为在每次迭代中,它在关联的内存中都有更多的pi数字。)

(从/programming//q/16250472/1858225移植)



我不这么认为。输入没有任何限制。
Kyle Strand

您是关于真正的运行时环境(例如JVM)还是关于以编程方式检测这种循环的方式的问题?

@Benj stackoverflow.com/q/16249785/1858225最初的问题(不是我的)是关于实时运行时环境(或更确切地说,是关于操作系统)的问题。但是,那件事已经结束了,所以我改写了一下,我把焦点转移到了理论上。
Kyle Strand

好。我看到的唯一方法是对一些关键点进行采样并对其进行哈希处理(这可能是日志输出的最后几行,或者是某些CPU状态,例如stack ptr),并存储一组探针(一组探针)的哈希值在给定的时间)。然后,您将能够(通过选择正确的“探针”)来检测循环锁定。我也在考虑挂钩系统库访问并将其条目用作探针。享受;)
Benj 2013年

Answers:


11

理论上讲,运行时环境可能会使用以下过程检查此类循环:

执行完指令后,运行时环境将对正在运行的进程的状态做出完整的映像(即与之相关的所有内存,包括寄存器,PC,堆栈,堆和全局变量),将该映像保存在某个地方,然后检查查看它是否与该过程以前保存的任何图像匹配。如果存在匹配项,则该过程将陷入无限循环。否则,将执行下一条指令并重复该过程。

实际上,运行时环境可以简单地定期暂停进程并进行保存状态,而不是在每条指令后都执行此检查。如果进程陷入涉及n个状态的无限循环中,那么在最多进行n次检查之后,将观察到重复状态。

当然,请注意,这不是解决暂停问题的方法。区别在这里讨论。

但是这样的功能将浪费大量资源 ; 持续暂停进程以保存与其相关的所有内存,将大大降低其运行速度,并非常迅速地消耗大量内存。(尽管旧图像可能会在一段时间后删除,但是限制可保存图像的总数是有风险的,因为如果循环太少,则无限大循环(即具有多个状态的无限循环)可能不会被捕获。状态存储在内存中。)此外,此功能实际上不会提供太大的好处,因为它捕获错误的能力将非常有限,并且使用其他调试方法(例如,单步执行代码)查找无限循环相对简单并识别出逻辑错误)。

因此,我怀疑这样的运行时环境是否存在,或者是否曾经存在过,除非有人为踢它编程。(现在我有些想这样做。)


8
程序可能(至少在Turing Machines等理想化的世界中)进入无限循环而不重复状态。想想像C循环之类的东西for(i = 0; ; i++) ;
vonbrand

nn<nn+1

@vonbrand,出于这个特定问题的目的,特定的循环不适合我对“循环”的定义(这就是为什么我在问题本身中明确定义了原因)。
Kyle Strand

n

也许我不明白你的问题。我以为您想知道是否有可能确定任何程序是否重复状态。您是在问是否可以确定某些程序是否重复状态吗?
Huck Bennett

6

让我们假设程序不与外界交互,因此确实有可能封装程序的整个状态。(这意味着它至少不做任何输入。)此外,我们假设程序在确定性环境中运行,以便每个状态都有一个唯一的后继者,这意味着运行时未线程化或线程化可以确定地简化为一个序列。

在这些极不可能的但理论上非限制性的假设下,我们可以复制程序并在两个独立的运行时中运行它。每个都将执行完全相同的计算。

因此,让我们这样做。我们将在Tortoise运行时中运行一次,同时在Hare运行时中运行它。但是,我们将安排Hare运行时的运行速度快一倍。每当Tortoise运行时执行一个步骤时,Hare运行时执行两个步骤。

npknkknp

测试的总成本是一个额外的状态和每个步骤一个状态的比较,并且测试终止的时间不得超过程序完成其第一个循环所需的步骤数的三倍。(在乌龟中一次,在野兔中两次,总共三遍。)

就像我使用的术语所暗示的那样,这只是罗伯特·弗洛伊德(Robert Floyd)著名的草龟和野兔周期检测算法。


3

就像我要建议弗洛伊德(Floyd)的循环检测算法一样,在rici的帖子中我也很满意。但是,通过加速对完整状态的比较,可以使整个过程变得更加实用。

所提出算法的瓶颈在于比较完整状态。这些比较通常不会完成,但是会尽早停止-在第一个差异处。一种优化是记住过去差异发生的位置,并首先检查状态的那些部分。例如,维护一个位置列表,并在进行完整比较之前浏览此列表。当此列表中的某个位置暴露出差异时,请停止比较(失败),然后将该位置移至列表的开头。

另一种(可能更具可扩展性)的方法是使用增量哈希。选择一个完整状态的函数,以便当状态的某些部分发生更改时,哈希值易于在O(1)中进行调整。例如,取状态词的加权总和mod一些大的素数,并与未加权的总和mod结合一些其他的大素数(也可以使用不同的权重和模数,添加单词平方的模块加权总和)。这样,散列更新将在每个执行步骤上花费O(1)时间,而比较将花费O(1)时间,直到您被点击为止。假阳性(即状态不同时哈希匹配)的可能性非常低,即使发生这种情况,也会摊销大量真阴性(不可能是假阴性)。

当然,在实践中,似乎更可能出现诸如生成pi位数的数字的情况-事情一直在变化,但永无止境。另一个常见的可能性是无限循环分配内存,在这种情况下,它会很快耗尽所有可用内存。

在我的算法和数据结构课程中,我们的自动分级机必须处理有时会陷入无限循环的学生提交的内容。可以通过30秒的超时和一定的内存限制来解决此问题。两者都比我们在分级中强加的运行时和内存预算宽松得多。我不确定在这种情况下实现真正的无限循环检测是否有意义,因为此类程序的运行速度会慢很多(这是对状态哈希的硬件支持可能会有所帮助的地方,但是同样,您还需要额外的用途来实现)证明这一点)。当学生知道他们的程序超时时,他们通常可以找到无限循环。


2

所述AProVE端接工具执行对重写系统(包括Haskell程序的一个子类),它可以静态分析证明非终止,得到非终止程序的一个实际的例子。该技术功能非常强大,并且可以使用变窄技术的变体来工作。

据我所知,在检测通用语言的实际非终止性方面还没有做很多工作。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.