SIGSEGV与递归算法

问题描述:

我实现了一个递归算法,它运行约1730年递归,然后崩溃与神秘的SIGSEGV。我想我的GDB,得到了以下的输出:SIGSEGV与递归算法

Program received signal SIGSEGV, Segmentation fault. 
0x000000000040b7da in Town::get_cur_capacity (this=0x61efe0) at ./solver/Darstellung.cpp:54 
54  return left_over_capacity; 
(gdb) print 0x61efe0 
$1 = 6418400 
(gdb) print *0x61efe0 
Cannot access memory at address 0x61efe0 
(gdb) print this 
$2 = (const Town * const) 0x61efe0 
(gdb) 

怎么会这样,调试器不知道它应该是一个const镇指针,但不能够访问内存给我一个转储?我很确定这种方法没有错误,因为它在崩溃之前使用了几千次,就像程序中的其他函数一样。有没有可能这是与操作系统相关的问题?我正在使用Linux Ubuntu 64位。

我的简化算法:

bool solveproblem(ptr_to_model) { 
     if(way_one(ptr_to_model)) 
       return true; 

     if(way_two(ptr_to_model)) 
       return true; 

     if(check_if_solved) 
       return true; 

     return false; 
} 

bool way_one(ptr_to_model) { 
    for(go_through_current_problem_configuration) { 
    if(check_stuff) { 
      ptr_to_model->execute_partial_solution(...); //adds another problem configuration to the stack within the model 
      if(solveproblem(ptr_to_model)) 
        return true; 

      ptr_to_model->redo_last_step(); 
    } 
    } 
    return false; 
} 

bool way_two(...) { 
    /*basicly the same as way one*/ 
} 

bool check_if_solve(...) { 
     if(problem_solved) 
       return true; 
     else 
       return false; 
} 

该模型是类同的名称,它代表所有通过推动其堆栈上一个新的“层”,其是经修饰的步骤通过时所作的算法(hopfully简化)问题,考虑到算法评估的部分解决方案。希望我把它缩小到可以理解的程度。

+0

这是一个非常大的递归深度!你能给我们简单的算法代码吗?在某些情况下,您可以将递归“展开”为无限循环。 – Blender

+4

这几乎可以肯定是你的代码有问题,而不是“操作系统相关的问题”。您可能会耗尽某些资源(例如堆栈空间),或者在代码中碰到一些特定的案例错误。没有看到代码是不可能的。 – NPE

+2

我发现通过将assert()放入您的代码来验证不变量,调试深度递归算法效果最佳。这样,当你遇到不希望发生的情况时,强迫它转储核心,而不是在很晚以后才能获得SIGSEGV。在这种情况下,断言也是可取的,因为你有1700个递归调用,你将很难找到一个不会碰到成千上万次的好断点。 – Kevin

如果你在递归中的层次深度是1700,那么你不可能超越你的栈并损坏了一个可能导致这种崩溃的调用参数。

如果您使用g ++,请尝试添加-fstack-protector-all以查看它是否有助于您获得更好的诊断。

编辑:另一个指标是如果你的内部gdb的回溯变成循环或不导致任何地方:这是一个强烈的指标,堆栈已经损坏。

并且在回应评论时,没有确定的方法来确定是堆栈溢出还是“更正常”的堆损坏。显然valgrind对于内存错误如果可用的话总是一个可靠的选项。您可以在您的shell中使用ulimit,或者(我相信)setrlimit以编程方式配置堆栈限制。请注意,存在硬限制上限,通常最好将递归更改为较少堆栈滥用,而不是增加堆栈大小。

+0

这是一个很好的方式来调试可能的堆栈粉碎我在我的答案中提到。 –

+0

应该有没有比通常的SIGSEGV任何其他输出?有没有办法增加堆栈大小? – Sim

您在栈上传递的参数有多大?在这个深度,如果你为5米长的8米堆栈传递,你可能会溢出。这对堆栈变量来说相当大,但可能。或者,您可能会通过写入超过存储在堆栈中的缓冲区的末尾(通常是字符串缓冲区)来粉碎堆栈。你在return崩溃的事实表明这是一种可能性。

+0

我正在使用标准堆栈实现(在我的模型中)并推送一个包含短变量和短变量向量的类。我使用了大约300MB的Ram,直到它崩溃。除了指针之外,没有其他的变量可以传递,如果你确实指的是函数本身。 – Sim