RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 573078
Accepted
AccumPlus
AccumPlus
Asked:2020-10-03 16:59:38 +0000 UTC2020-10-03 16:59:38 +0000 UTC 2020-10-03 16:59:38 +0000 UTC

在 Python 中拦截信号

  • 772

有两个程序通过命名管道相互通信。一种在 C++ 中,一种在 Python 中。此外,第一个启动第二个(以标准方式,通过 fork + exec)。

父程序交流区(C++):

// ...
int pipeDescr;
std::string outputPipeName{"inputPipe"};
std::string inputPipeName{"outputPipe"};
char *message = new char[BUFSIZE];
// ...
while (true)
{
    int bytesNumber = 0;
    // ...
    if ((pipeDescr = open(outputPipeName.c_str(), O_WRONLY)) <= 0)
        break;

    bytesNumber = write(pipeDescr, message, strlen(message));
    if (bytesNumber <= 0)
        break;

    close(pipeDescr);

    message[0] = '\0';
    if ((pipeDescr = open(inputPipeName.c_str(), O_RDONLY)) <= 0)
        break;

    bytesNumber = read(pipeDescr, message, BUFSIZE);
    if (bytesNumber <= 0)
        break;

    close(pipeDescr);
    // ...
}

watchDog函数运行在一个单独的线程中,负责Python中子应用程序的运行。

void watchDog(int clientSocket, pid_t pid, bool &stopWatchDog)
{
    while (true)
    {
        // Дочерняя программа завершилась с ошибкой
        if (waitpid(pid, NULL, WNOHANG) != 0)
        {
            // ...
        }

        // Родительская программа закрывается
        mutexClosing.lock();
        if (closing)
        {
            mutexClosing.unlock();
            if (waitpid(pid, NULL, WNOHANG) == 0)
            {
                kill(pid, SIGTERM);
                waitpid(pid, NULL, 0);
            }
            break;
        }
        mutexClosing.unlock();

        // Программное отключение WatchDog-а
        mutexWatchDog.lock();
        if (stopWatchDog)
        {
            if (waitpid(pid, NULL, WNOHANG) == 0)
            {
                kill(pid, SIGTERM);
                waitpid(pid, NULL, 0);
            }
            mutexWatchDog.unlock();
            break;
        }
        mutexWatchDog.unlock();
    }
}

这样做有以下三种结果:

  • 子程序崩溃

  • 父程序的终止

  • 在不终止父程序的情况下终止子程序

最后两个选项要求子程序正常终止,所以我向它发送一个 SIGTERM 信号并等待它完成。

子程序通信部分(Python):

def sigterm_handler(signal, frame):
    print('\nGot sigterm!\n')
    sys.exit(0)

def main():
    input_pipe_name = "inputPipe"
    output_pipe_name = "outputPipe"
    # ...
    signal.signal(signal.SIGTERM, sigterm_handler)
    # ...
    while True:
        pipe_descr = os.open(input_pipe_name, os.O_RDONLY)
        request = os.read(pipe_descr, 10000)
        os.close(pipe_descr)

        reply = work_func(request)

        pipe_descr = os.open(output_pipe_name, os.O_WRONLY)
        os.write(pipe_descr, bytes(reply, 'UTF-8'))
        os.close(pipe_descr)

我在代码中添加了 SIGTERM 信号的处理。但是,发送此信号时,不会调用 sigterm_handler 函数。

但!如果你写这样的东西:

def main():
    signal.signal(signal.SIGTERM, sigterm_handler)

    while True:
        print('waiting...')
        time.sleep(2)

该函数将被调用。

请告诉我如何解决这个问题!

c++
  • 2 2 个回答
  • 10 Views

2 个回答

  • Voted
  1. Best Answer
    jfs
    2020-10-04T01:20:02Z2020-10-04T01:20:02Z

    当接收到信号时,C 处理程序设置一个标志并立即退出(下文中,我描述了 Python 实现)。用 Python 编写的处理程序仅在控制返回主解释器线程时执行,这会稍后发生(例如在以下字节码中)或永远不会发生。引用官方文档:

    Python 信号处理程序不会在低级 (C) 信号处理程序中执行。相反,低级信号处理程序设置一个标志,告诉虚拟机稍后执行相应的 Python 信号处理程序(例如在下一个字节码指令)

    何时返回控制权取决于当时执行的位置(不同平台上不同版本的 Python 上的不同函数可能有不同的行为)。例如,os.open()在 Python 3.5 的 POSIX 系统上,它归结为:

    do {
        Py_BEGIN_ALLOW_THREADS
        fd = open(path->narrow, flags, mode);
        Py_END_ALLOW_THREADS
    } while (fd < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
    

    Py_BEGIN_ALLOW_THREADSopen(2)该宏释放了 GIL,它允许其他线程在当前线程被系统调用阻塞时执行 Python 代码。

    设置它的处理程序时,signal.signal()调用会重置SA_RESTART标志,这样系统调用就会open(2)被信号和 return 中断EINTR,在这种情况下调用PyErr_CheckSignals()一个函数,除非调用来自程序的主线程,否则什么都不做。在主线程PyErr_CheckSignals()中,它检查是否有信号(通过 C 处理程序设置的标志)并调用用 Python 编写的处理程序。如果处理程序抛出异常,它PyErr_CheckSignals()返回非零值并且循环中断,导致从主线程调用os.open时 Python 代码中的调用站点出现异常(否则返回)。os.openPyErr_CheckSignals()0

    在其他情况下,可能有很多选项:由GIL释放/不释放(在C代码中),阻塞调用本身是否被中断/自动重启(可能取决于平台,C库,Python版本),是否是在主线程中调用PyErr_CheckSignals(),无论它是否在某处重置,然后标记信号发生在调用处理程序之前(signal(SIGINT, custom_handler)在 Python 2.7 上的 Windows 上不起作用)。

    如果您不能将阻塞的 C 代码更改为PyErr_CheckSignals()像os.open接收信号时那样调用,那么要解决这个问题:在后台线程和主线程上调用阻塞代码,以短时间间隔休眠(这不会如果你的 CPython 扩展没有释放 GIL,那将无济于事,例如re,一个模块可能会这样做):

    import threading
    
    background_thread = threading.Thread(target=fifo_loop)
    background_thread.daemon = True
    background_thread.start()
    while background_thread.is_alive():
        background_thread.join(1)
        # здесь никакого другого кода, это весь цикл
    

    请注意,这与添加time.sleep(1)到循环的建议不同,后者适用于FIFO(7)。如果信号发生在您的 fifo 循环中的调用之外time.sleep(1),那么问题将仍然存在。

    解决方法之所以有效,是因为 fifo 循环在后台线程上运行,而主线程只是间歇性休眠。在 Python 3 中,它可以background_thread.join()不被使用timeout(在 Python 2 中,这个.join()没有被信号中断)。

    • 5
  2. Senior Pomidor
    2020-10-03T21:47:14Z2020-10-03T21:47:14Z

    在while True:您需要添加 之后time.sleep(1),因为您需要稍等片刻才能向该过程发出信号。

    ps 摘自评论

    • 2

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    如何停止编写糟糕的代码?

    • 3 个回答
  • Marko Smith

    onCreateView 方法重构

    • 1 个回答
  • Marko Smith

    通用还是非通用

    • 2 个回答
  • Marko Smith

    如何访问 jQuery 中的列

    • 1 个回答
  • Marko Smith

    *.tga 文件的组重命名(3620 个)

    • 1 个回答
  • Marko Smith

    内存分配列表C#

    • 1 个回答
  • Marko Smith

    常规赛适度贪婪

    • 1 个回答
  • Marko Smith

    如何制作自己的自动完成/自动更正?

    • 1 个回答
  • Marko Smith

    选择斐波那契数列

    • 2 个回答
  • Marko Smith

    所有 API 版本中的通用权限代码

    • 2 个回答
  • Martin Hope
    jfs *(星号)和 ** 双星号在 Python 中是什么意思? 2020-11-23 05:07:40 +0000 UTC
  • Martin Hope
    hwak 哪个孩子调用了父母的静态方法?还是不可能完成的任务? 2020-11-18 16:30:55 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    user207618 Codegolf——组合选择算法的实现 2020-10-23 18:46:29 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    Arch ArrayList 与 LinkedList 的区别? 2020-09-20 02:42:49 +0000 UTC
  • Martin Hope
    iluxa1810 哪个更正确使用:if () 或 try-catch? 2020-08-23 18:56:13 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5