【问题标题】:How to abort Python script calling C function using Swig?如何使用 Swig 中止调用 C 函数的 Python 脚本?
【发布时间】:2012-08-27 16:05:38
【问题描述】:

我在尝试中止 Python 脚本时遇到问题,例如:

#!/usr/bin/python

import hello

hello.draw()

exit()

其中 draw() 是一个 C 函数。在这个函数中有一个循环来打印一些文本。我尝试 CTRL-C 来摆脱它,但它不起作用。 重要的是不能修改C代码!

有什么想法吗?

【问题讨论】:

    标签: python c swig abort


    【解决方案1】:

    我整理了一个示例,说明您可以如何做到这一点。它可以增强功能并从中逃脱,而无需对其进行修改。例如,给定函数 foo(在 test.h 中内联用于我的测试):

    void foo() {
      while(1); // never returns
    }
    

    您可以使用信号和siglongjmp 玩游戏。不过需要注意的是,you need to be very careful 确保您只在 foo 中调用 async safe 函数,或者在任何不安全的调用之前屏蔽信号并在任何不安全调用之后取消屏蔽它们。

    然后我们可以包装函数并使用%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 一起使用 - 原因是它没有't 是因为how the default Python signal handlers work。 Python 使用的默认处理程序只是设置一个标志并在底层调用将控制权返回给 Python 之后处理信号,这巧妙地避开了整个异步安全问题。

    【讨论】:

    • 这真是太好了。我花了相当长的时间处理一个使用 sigwait 等待信号的线程。我不得不回到老式的 sigsetjmp。不知道使用 SWIG 的 %exception 构造。很酷
    【解决方案2】:

    您需要为您的 C 函数提供一种中止它的方法 - 例如,它可以在其绘制循环中轮询全局状态标志。

    【讨论】:

    • 哦,是的,你也可以这样做。
    • 认真的吗?所以你真的想运行只循环打印hello world的c代码?
    • 这只是示例。我尝试使用一个更大的应用程序中的一些功能,它读取并打印一些有关网络的信息(IP、端口、协议……)。这个文件有 50MB。因此,打印和阅读将结束,但在某些情况下我需要停止它。
    【解决方案3】:

    另一种可能性是停止进程然后终止它。在bash 命令行 (Linux/MacOS) 中,如下所示:

    假设您启动脚本...

    $ python3 example.py
    

    ...然后键入 control+Z 以停止该过程。查找方括号中的数字,在本例中为1

    [1]+  Stopped                 python3 example.py
    

    现在您可以使用kill 命令终止该进程,后跟方括号中显示的数字。

    $ kill %1
    [1]+  Terminated: 15          python3 example.py
    

    【讨论】:

      【解决方案4】:

      这里是 C++ 的 siglongjmp 的另一种方法,但也可以在没有 lambda 的情况下轻松适应 C:

      %exception {
          signal(SIGINT, [](int signum){
              PyErr_SetNone(PyExc_KeyboardInterrupt);
              Py_Finalize();
              exit(1);
          });
          $action
      };
      

      【讨论】:

        猜你喜欢
        • 2023-04-07
        • 2021-06-14
        • 2016-03-06
        • 1970-01-01
        • 1970-01-01
        • 2021-08-11
        • 2018-08-04
        • 1970-01-01
        • 2021-07-23
        相关资源
        最近更新 更多