我在尝试中止 Python 脚本时遇到问题,例如:
#!/usr/bin/python
import hello
hello.draw()
exit()
其中 draw() 是一个 C 函数。在这个函数中有一个循环来打印一些文本。我尝试 CTRL-C 来摆脱它,但它不起作用。重要的是不能修改 C 代码!
有任何想法吗?
我举了一个例子来说明你如何做到这一点。它可以增强功能并从中逃脱,而无需对其进行修改。例如,给定函数foo
(在 test.h 中内联用于我的测试):
void foo() {
while(1); // never returns
}
您可以使用信号和siglongjmp
. 但是应该注意的是,您需要非常小心,以确保您只从内部调用异步安全函数foo
,或者在任何不安全调用之后屏蔽信号并取消屏蔽它们。
然后我们可以包装函数并使用%exception
注入一些额外的代码来允许信号退出函数:
%module test
%{
#include <setjmp.h>
#include <signal.h>
static sigjmp_buf timeout;
static void backout(int sig) {
siglongjmp(timeout, sig);
}
#include "test.h"
%}
%include <exception.i>
%exception {
if (!sigsetjmp(timeout, 1)) {
signal(SIGALRM,backout); // Check return?
$action
}
else {
// raise a Python exception
SWIG_exception(SWIG_RuntimeError, "Timeout in $decl");
}
}
void foo();
%exception;
这允许我们写:
import signal
import test
signal.alarm(5)
test.foo()
考虑到有关异步安全功能的警告,这可以按预期工作。我SIGALRM
在这里使用它是因为它允许我将它全部保存在一个 Python 文件中。如果您愿意,可以使用另一个线程/进程和其他信号。
如果您将我的示例中的 SIGALRM 更改为 SIGINT (并且显然也不要引发 SIGALRM),它将与Ctrl+一起使用C- 它不适用的原因是因为默认 Python 信号处理程序的工作方式。Python 使用的默认处理程序只是设置一个标志并在底层调用将控制权返回给 Python后处理信号,这巧妙地避开了整个异步安全问题。
您需要为您的 C 函数提供一种中止它的方法 - 例如,它可以在其绘制循环中轮询全局状态标志。
另一种可能性是停止该进程然后终止它。在bash
命令行 (Linux/MacOS) 中,它看起来像这样:
假设您启动脚本...
$ python3 example.py
...然后键入control+Z以停止该过程。查找方括号中的数字,在本例中为1
。
[1]+ Stopped python3 example.py
现在您可以使用命令终止该进程kill
,后跟方括号中显示的数字。
$ kill %1
[1]+ Terminated: 15 python3 example.py
这是siglongjmp
C++ 的另一种方法,但也可以在没有 lambda 的情况下轻松适应 C:
%exception {
signal(SIGINT, [](int signum){
PyErr_SetNone(PyExc_KeyboardInterrupt);
Py_Finalize();
exit(1);
});
$action
};