在SIGINT

时间:2018-05-23 15:52:26

标签: python

以下代码作为Saving work after a SIGINT

的参考
class Main(object):
    def do_stuff(self):
        ...
    def save_work(self):
        ...
    def __init__(self):
        try:
            self.do_stuff()
        except KeyboardInterrupt:
            pass # Or print helpful info
        self.save_work()

如果没有子进程,这种方法非常好。

但是,只要在save_work()中调用子进程,子进程就不会在收到SIGINT信号时执行。

所以,执行

    cmd = r"hadoop fs -put '{}' '{}'".format(
        src, dest)
    process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

无效。

该解决方法是什么?

2 个答案:

答案 0 :(得分:0)

如上所述对您的问题/问题的最短答案:将subprocess.Popen替换为subprocess.call或其中一个(例如检查)变体。或者添加process.communicate()

发生了什么事,为什么它看起来像#34;不起作用"。 Popen打开了用于通信的管道,并根据需要分叉了一个进程。但是,管道在父进程端(你从中调用它)没有任何东西读取它实际上可以导致子进程(写入stdout / stderr)非常快速地进入阻塞I / O.与此同时,您的父级继续运行,因为没有任何东西告诉它等待其子级,并最终终止子进程收到SIGPIPE(默认操作将终止)。

我们有test.sh

#!/bin/bash
handle_sigpipe() {
        echo "GOT SIGPIPE" >> OUT
        exit 0
}
trap handle_sigpipe SIGPIPE
echo line1 > OUT
echo line2
echo line3 >> OUT

和一个小python脚本调用它类似于你的问题:

import time
import subprocess
try:
    time.sleep(20)
except KeyboardInterrupt:
    cmd = "./test.sh"
    process = subprocess.Popen(cmd, shell=True,
                               stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

现在我们打电话并打断它:

$ python3 test.py 
^C
$ cat OUT 
line1
GOT SIGPIPE

我们已经写了line1,但是当脚本尝试编写line2时,它最终会等待有人从接收和管道中读取(至少在通过shell调用时) ,这些stdout写入是行缓冲的)。同时,父母完成并关闭管道的末端。 test.sh收到SIGPIPE,处理程序将其写入文件,shell脚本终止。

如果您实际上通常希望在脚本退出时执行清理/保存工作(也是在中断时)。 atexit是这样做的常用方法。如果您想要处理特定信号(例如SIGINT),您还可以查看signal.signal

答案 1 :(得分:0)

上述问题是由重构代码解决的。级联异常阻止了信号。

最终的解决方案是。

def signal_handler(sign, frame):
    logger.warn('app has been terminated manually with signal {} at frame {}'.format(sign, frame))
    sys.exit(1)

def end_programm():
    upload_log_file()


def main():
    [...]
    signal.signal(signal.SIGINT, signal_handler)
    signal.signal(signal.SIGTERM, signal_handler)
    atexit.register(end_programm)
    [...]