能否捕获分段错误?

4

我的应用程序依赖于ghostscript将一些pdf文件转换为每个文档页面的一系列图像。这是一个简化版本:

import locale

from ghostscript import Ghostscript as gs
from ghostscript import cleanup
from cv2 import imread, IMREAD_GRAYSCALE as GRAY
from multiprocessing import cpu_count

args = [
  "",
  "-q", "-r300", "-dNOPAUSE",
  "-sDEVICE=pgmraw",
  "-sOutputFile=%d.pgm",
  "-dNumRenderingThreads=" + str(cpu_count()),
  "-f", "_.pdf" #filename will always be "_.pdf"
]
encoding = locale.getpreferredencoding()
args = [a.encode(encoding) for a in args]

def pdftoimarray():
    cleanup()
    gs(*args)
    imarray = []
    for filename in os.listdir():
        imarray.append(imread(filename, GRAY))
    return imarray

(我有意删除了文件系统清理部分:对于这个问题来说并不重要)

问题是,我不能真正信任这些文档的来源,其中一些可能存在缺陷。通过运行一些测试,我发现其中一些错误文档会导致ghostscript实际上出现段错误,从而使我的整个应用程序崩溃。

通常,段错误是一个非常严重的事件,我们无法真正从中恢复,所以我怀疑是否真的有可能捕获它。但在我的情况下,它不应该真的那么严重:假设我的程序仍处于有效状态,我可以只将那个文档标记为并继续进行。

问题:我是否可以在依赖项中捕获此段错误,并从中恢复?

这在Segmentation Fault Catch中已经被问过了,但唯一的答案是错误的(它建议使用signal.signal来捕获它,但文档明确指出,使用它捕获同步信号(如SIGSEGV)没有多大意义。同样的文档指向faulthandler,但它不能真正捕获信号:如果它发生,它只提供更好的错误消息)。

这就留下了一个问题,即这个问题独特在哪里,而不是一个重复的问题:我受限制较少:我根本不打算解决这个问题:我只想忽略它并继续进行。任何有关实际避免ghostscript中的段错误的提示也将非常受欢迎。

这个问题有点老了,但我认为我应该分享一下:我正在观看有关一个很酷的新的内存分配器的视频,在听众提出的一个问题中,作者解释说他“安装了一个段错误处理程序”,这正是我感兴趣的内容。我仍然不知道他到底是如何做到的,所以这并没有完全回答我的问题,但它为我开始研究提供了一个好的起点。如果我成功地解决了这个问题,我会在这里发布一个答案。

以下是视频链接(链接指向我提到的问题的时间):https://youtu.be/c1UBJbfR-H0?t=2058


1
我认为你不能忽略它。你的代码试图访问不属于它的内存。在这种情况下,操作系统应立即将其终止。 - ForceBru
@ForceBru 我原本指望操作系统只会杀掉真正的罪犯(即Ghostscript依赖项),并让Python解释器保持不变(这就是像faulthandler这样的库在接收到信号后能够实际执行一些操作然后退出的原因)。我的假设错了吗? - Cássio Renan
1
你可能想将Ghostscript作为外部进程调用,这样它就与Python隔离开来。如果它崩溃了,Python也不会受到损坏。在进程内运行意味着整个Python进程都会受到段错误的影响。 - John Kugelman
关于避免Ghostscript中的段错误,这显然是一个bug,因此您应该提交一个bug报告。如果您不这样做,那么问题将永远无法解决...(当然,您还应确保使用最新的软件)。 - KenS
对于任何想知道的人:当时,我们决定转向MuPdf。因此,虽然我不再需要这个答案来完成项目,但我仍然很想知道是否可能。 - Cássio Renan
显示剩余10条评论
1个回答

2

我遇到了类似的问题,通过pythonocc渲染cad文件时有时脚本会崩溃。非常烦人。你需要手动删除文件并重新启动批处理。

基本上的思路是为任务启动一个额外的进程,并检查它的退出码

import multiprocessing as mp


def do_stuff_that_segfaults(param):
    call_shitty_library(param)

def main():
    p = mp.Process(target=do_stuff_that_segfaults, args=param)            
    p.start()            
    p.join()
    if p.exitcode == -11:  # Segmentation fault
        do_stuff_in_case_of_segfault()

我也尝试了其他建议,比如你提供的 Segmentation Fault Catch,但都没有成功。我真的很想使用 mp.pool() 来使用所有核心,但是从 mp.pool() 中无法获取退出状态。
到目前为止,代码运行良好,并通过 do_stuff_in_case_of_segfault() 将导致 segfault 的文件移动到另一个文件夹中,而不会使我的主脚本被杀死。

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