什么导致Python分段错误?

121

我在Python中实现了Kosaraju的强连通分量(SCC)图搜索算法。

该程序对小数据集运行良好,但是当我在超大型图形上运行它(超过800,000个节点)时,会出现“分段错误”。

可能的原因是什么?谢谢!


附加信息: 当我在超大数据集上运行时,首先遇到了这个错误:

"RuntimeError: maximum recursion depth exceeded in cmp"

然后我使用以下代码重置递归限制:

sys.setrecursionlimit(50000)

但是出现了“分段错误”

相信我,这不是一个无限循环,在相对较小的数据上运行是正确的。可能是程序耗尽了资源吗?


13
你可以看一下CrashingPython - Abhijit
2
这是在纯Python中运行还是使用了C扩展模块?如果是纯Python,那么这里就有一个bug,恭喜你。如果你正在使用C模块,那么segfault可能来自于那里。 - aaronasterling
这是纯Python编写的程序。该程序在相对较小的数据集上运行良好,让我认为代码是正确的。 - xiaolong
根据Python文档: - James Thiele
2
根据Python文档:最高可能限制取决于平台。当用户有一个需要深度递归和支持更高限制的平台的程序时,她可能需要将限制设置得更高。这应该小心进行,因为太高的限制可能会导致崩溃。您没有指定操作系统。对“崩溃”的引用可能意味着在您的操作系统上出现了“分段错误”。尝试使用较小的堆栈。但是,如果我没记错,您正在使用的算法将整个SSC放在堆栈上,因此可能会耗尽堆栈。 - James Thiele
@MattyW 是的。后来我将Python翻译成C/C++,并且在将图形存储为全局变量时没有发现问题。似乎Python更依赖于系统堆栈。请参见解决方案代码 - xiaolong
8个回答

98

当一个用C语言编写的Python扩展尝试访问超出其范围的内存时,就会发生这种情况。

您可以通过以下方式跟踪它。

  • 在代码的第一行添加 sys.settrace
  • 按照Mark在此回答中描述的方法使用gdb。 在命令提示符下:

gdb python
(gdb) run /path/to/script.py
## wait for segfault ##
(gdb) backtrace
## stack trace of the c code

2
谢谢,但我的代码是纯Python的,这会有什么区别吗? - xiaolong
1
类似的,也很有帮助:stdlib的trace模块帮助我在一个暂存服务器上找到了段错误的根源,而不需要安装新的依赖项,也不需要修改代码。 - floer32
7
在OSX Sierra操作系统中,gdb被lldb所替代。 - kthouz
sys.settrace 需要 1 个参数。那是什么? - Eduardo Reis
@EduardoReis 这个参数是一个追踪函数。请查看我在答案中提供的文档链接。 - Shiplu Mokaddim
显示剩余2条评论

67

我知道您已解决了问题,但是为了其他读者,请看以下答案:您需要增加操作系统为python进程分配的堆栈大小。

具体方法因操作系统而异。在Linux中,您可以使用命令ulimit -s检查当前值,并使用ulimit -s <new_value>来增加它。

尝试将先前的值加倍,如果不起作用,请继续加倍,直到找到有效的值或内存耗尽为止。


另外一个检查是否达到ulimit最大值的好方法是运行lsof并使用grepwc -l来跟踪所有内容。 - cdated
我同意。这个方法在我的Kosaraju的SCC实现中真的起作用了,修复了Python和C++实现中的段错误问题。 <br/> 对于我的MAC,我通过以下方式找到了可能的最大值: - Rock
4
请注意,ulimit 值仅在执行该命令的特定 shell 中进行修改,以免意外修改整个系统的值。 - Tanmay Garg
2
我执行了 ulimit -s 16384 命令,但是在运行后仍然出现了分段错误。 - Sreehari R
@SreehariR 尝试再增加一些。但是如果您正在使用任何Python扩展,可能也会出现问题,这篇(其他答案)[https://dev59.com/PWkw5IYBdhLWcg3wPIJ7#10035594]建议如何进行调试。 - Davide

28

段错误是一个通用错误,有很多可能的原因:

  • 内存不足
  • 损坏的RAM内存
  • 使用查询从数据库中提取大量数据集(如果提取的数据大小超过了交换内存)
  • 错误的查询/有缺陷的代码
  • 拥有长循环(多重递归)

6

通过更新ulimit,我的Kosaraju的SCC实现得到了修复,解决了Python和C++实现中的段错误问题(Python居然也会出现段错误!)。

对于我的MAC,我通过以下方式找到了可能的最大值:

$ ulimit -s -H
65532

如何更新该值?该值是以什么类型的单位表示的? - Pablo
1
如果你非常了解你需要的限制(并且你知道你的平台永远不会改变为linux),你可以使用Python执行命令来在代码内部执行该命令。我个人已经将它添加到我的.bashrc文件中。 - trumpetlicks

5
Google搜索给我找到了这篇文章,但我没有看到以下“个人解决方案”被讨论过。
我最近对Windows子系统上的Python 3.7感到恼火,因为在两台机器上使用相同的Pandas库时,一台会给我分段错误,而另一台则报告警告。不清楚哪个是更新的版本,但“重新安装”pandas可以解决问题。

我在有问题的机器上运行的命令。

conda install pandas

更多细节:我在两台Windows 10机器上运行完全相同的脚本(通过Git同步),都使用WSL + Anaconda。以下是截图以证明。此外,在命令行python会抱怨Segmentation fault (core dumped)的机器上,Jupyter lab每次都会简单地重新启动内核。更糟糕的是,根本没有任何警告。

enter image description here


几个月后的更新:我不再在Windows机器上托管Jupyter服务器。我现在使用WSL在Windows上获取在Linux服务器上打开的远程端口,并在远程Linux机器上运行所有作业。在相当长的一段时间里,我从未遇到任何执行错误 :)


1
我在升级RPI上的dlib后遇到了这个分段错误。如Shiplu Mokaddim所建议的那样回溯堆栈,最终确定是OpenBLAS库的问题。
由于OpenBLAS也是多线程的,将其用于多线程应用程序将指数级增加线程数量,直到出现分段错误。对于多线程应用程序,请将OpenBlas设置为单线程模式。
在python虚拟环境中,通过编辑以下内容告诉OpenBLAS仅使用一个线程:
    $ workon <myenv>
    $ nano .virtualenv/<myenv>/bin/postactivate

并添加:

    export OPENBLAS_NUM_THREADS=1 
    export OPENBLAS_MAIN_FREE=1

重启后,我能够在rpi3b上运行之前会导致崩溃的所有图像识别应用程序。

reference: https://github.com/ageitgey/face_recognition/issues/294


0

我也遇到了同样的错误。我从另一个SO答案中学到,你需要通过sysresource模块设置递归限制。


你的回答可以通过添加更多支持信息来改进。请编辑并添加进一步的细节,例如引用或文档,以便他人可以确认你的答案是正确的。您可以在帮助中心找到有关如何编写良好答案的更多信息。 - jahantaila

0

看起来你的堆栈内存不够了。正如Davide所说,你可能想要增加它。在Python代码中实现这个,你需要使用线程来运行你的"main()"函数:

def main():
    pass # write your code here

sys.setrecursionlimit(2097152)    # adjust numbers
threading.stack_size(134217728)   # for your needs

main_thread = threading.Thread(target=main)
main_thread.start()
main_thread.join()

来源:c1729在codeforces上的帖子。使用PyPy运行它有点棘手


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接