【问题标题】:How to stop SIGINT being passed to subprocess in python?如何停止 SIGINT 被传递给 python 中的子进程?
【发布时间】:2010-07-12 21:57:58
【问题描述】:

我的 python 脚本使用信号 process 模块拦截 SIGINT 信号以防止过早退出,但该信号被传递给我使用 Popen 打开的子进程。有什么方法可以防止将此信号传递给子进程,以便在用户按下 ctrl-c 时它也不会过早退出?

【问题讨论】:

    标签: python subprocess signals intercept sigint


    【解决方案1】:

    启动子进程时会继承信号处理程序,因此如果您使用信号模块忽略 SIGINT (signal.signal(signal.SIGINT, signal.SIG_IGN)),那么您的子进程也会自动忽略。

    不过,有两个重要的警告:

    • 您必须在生成子进程之前设置忽略处理程序
    • 自定义信号处理程序被重置为默认处理程序,因为子进程将无权访问处理程序代码来运行它。

    因此,如果您需要自定义处理 SIGINT 而不仅仅是忽略它,您可能希望在生成子进程时暂时忽略 SIGINT,然后(重新)设置自定义信号处理程序。

    如果您尝试捕获 SIGINT 并设置一个标志,以便您可以在安全点而不是立即退出,请记住,当您到达该安全点时,您的代码将不得不手动清理其后代,因为您的孩子进程及其启动的任何进程都将忽略 SIGINT。

    【讨论】:

    • 如果这可行(它并不总是),它是一个比使用终端更好的选择。
    • 如果使用 fork()/exec() 而不是 popen(),您可以在 fork() 之后 exec() 之前执行 SIG_IGN。这样可以避免暂时禁用父级中的处理程序。似乎无论你做什么都会有竞争条件,但是 fork()/exec() 方式似乎更安全一些。当然,OP 是在询问 python 的 Popen,但这个问题似乎真的是关于 Posix;这就是我来这里的原因。
    • @thejoshwolfe:为了避免竞争条件,您使用 sigprocmask() 来延迟信号,设置处理程序,分叉,设置处理程序,然后再次使用 sigprocmask 重新启用信号。 (如果在调用 sigprocmask 之间有任何信号到达,它们将在此时传递。)
    • 请注意,子进程的子进程可能仍会收到 SIGINT(例如,apt-getpython 中作为子进程启动可能会产生 dpkggpgv)。在这种情况下,需要通过将preexec_fn=os.setpgrp 传递给subprocess.Popen 来创建一个新的进程组,并在父信号处理程序中处理所有事情(包括手动信号转发到子进程)。
    【解决方案2】:

    您可以使用tty 模块重新分配 ctrl-c 的角色,该模块允许您操纵信号的分配。但是请注意,除非您将它们恢复到修改之前的状态,否则它们将在 shell 的整个会话期间持续存在,即使在程序退出后也是如此。

    这里有一个简单的代码 sn-p 让您开始,它存储您的旧 tty 设置,将 ctrl-c 重新分配给 ctrl-x,然后在退出时恢复您以前的 tty 设置。

    import sys
    import tty
    
    # Back up previous tty settings
    stdin_fileno = sys.stdin.fileno()
    old_ttyattr = tty.tcgetattr(stdin_fileno)
    
    try:
        print 'Reassigning ctrl-c to ctrl-x'
    
        # Enter raw mode on local tty
        tty.setraw(stdin_fileno)
        raw_ta = tty.tcgetattr(stdin_fileno)
        raw_ta[tty.LFLAG] |= tty.ISIG
        raw_ta[tty.OFLAG] |= tty.OPOST | tty.ONLCR
    
        # ^X is the new ^C, set this to 0 to disable it entirely
        raw_ta[tty.CC][tty.VINTR] = '\x18'  
    
        # Set raw tty as active tty
        tty.tcsetattr(stdin_fileno, tty.TCSANOW, raw_ta)
    
        # Dummy program loop
        import time
        for _ in range(5):
            print 'doing stuff'
            time.sleep(1)
    
    finally:
        print 'Resetting ctrl-c'
        # Restore previous tty no matter what
        tty.tcsetattr(stdin_fileno, tty.TCSANOW, old_ttyattr)
    

    【讨论】:

      【解决方案3】:

      对于 python 2 代码库:子进程已损坏。

      正确的是

      import subprocess32 as subprocess
      

      subprocess32

      这是用于 Python 的 Python 3 子进程模块的反向移植 2. 此代码尚未在 Windows 或其他非 POSIX 平台上测试。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-11-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-06-30
        • 2023-03-12
        • 2019-07-12
        • 2021-06-13
        相关资源
        最近更新 更多