允许Ctrl-C中断python C扩展

时间:2013-02-05 11:59:52

标签: python linux python-c-api

我在(自制的)基于C的python扩展中运行一些计算量很大的模拟。偶尔我会弄错,想终止模拟。但是,Ctrl-C似乎没有任何效果(除了将^C打印到屏幕之外,因此我必须使用kill或系统监视器终止进程。

据我所知,python只是等待C扩展完成,并且在此期间没有与它进行真正的通信。

有没有办法让这项工作?

4 个答案:

答案 0 :(得分:6)

Python在SIGINT上安装了一个信号处理程序,它只是设置一个由主解释器循环检查的标志。要使此处理程序正常工作,Python解释器必须运行Python代码。

您有几种选择:

  1. 使用Py_BEGIN_ALLOW_THREADS / Py_END_ALLOW_THREADS在C扩展程序代码周围发布GIL。不保存GIL时不能使用任何Python函数,但Python代码(和其他C代码)可能与C线程并行运行(真正的多线程)。一个单独的Python线程可以与C扩展一起执行并捕获Ctrl + C信号。
  2. 设置您自己的SIGINT处理程序并调用原始(Python)信号处理程序。然后,您的SIGINT处理程序可以执行任何操作,以取消C扩展代码并将控制权返回给Python解释器。

答案 1 :(得分:5)

  

但是,Ctrl-C似乎没有任何效果

Ctrl-C in the shell sends SIGINT to the foreground process group。接收信号时python在C代码中设置一个标志。如果您的C扩展在主线程中运行,那么将不会运行Python信号处理程序(因此您不会在KeyboardInterrupt上看到Ctrl-C异常),除非您调用PyErr_CheckSignals()检查标志(它意味着:它不应该让你慢下来)并在必要时运行Python信号处理程序,或者如果你的模拟允许Python代码执行(例如,如果模拟使用Python回调)。如果扩展在后台线程中运行,则释放GIL就足够了(允许Python代码在主线程中运行,使信号处理程序能够运行)。

相关:Cython, Python and KeybordInterrupt ingored

答案 2 :(得分:2)

我会重新设计C扩展,以便它们不会运行很长时间。

因此,将它们分成更基本的步骤(每个步骤运行一小段时间,例如10到50毫秒),并使用Python代码调用这些更基本的步骤。

作为一种编程风格,

continuation passing style可能与理解相关......

答案 3 :(得分:2)

如果您不希望将C扩展(或ctypes DLL)绑定到Python,则有另一种方法可以解决此问题,例如,您要创建带有多种语言绑定的C库的情况,必须允许您的C Extension长时间运行,然后您可以修改C ​​Extension:

在C扩展中包含信号头。

#include <signal.h>

在C扩展程序中创建信号处理程序typedef。

typedef void (*sighandler_t)(int);

在C扩展中添加信号处理程序,该信号处理程序将执行中断任何长时间运行的代码(设置停止标志等)所需的操作,并保存现有的Python信号处理程序。

sighandler_t old_sig_int_handler = signal(SIGINT, your_sig_handler);
sighandler_t old_sig_term_handler = signal(SIGTERM, your_sig_handler);

每当C扩展返回时,恢复现有的信号处理程序。此步骤可确保重新应用Python信号处理程序。

signal(SIGINT, old_sig_int_handler);
signal(SIGTERM, old_sig_term_handler);

如果长时间运行的代码被中断(标志等),请使用指示信号编号的返回码将控制返回给Python。

return SIGINT;

在Python中,发送在C扩展中收到的信号。

import os
import signal

status = c_extension.run()

if status in [signal.SIGINT, signal.SIGTERM]:
    os.kill(os.getpid(), status)

Python将执行您期望的操作,例如为SIGINT引发KeyboardInterrupt。