2016-02-17 25 views
3

时如何让蟒蛇子进程的标准输出,我有以下简单的Python脚本:receving SIGUSR2/SIGINT

import os, subprocess,signal,sys 
import time 

out = None 
sub = None 

def handler(signum,frame): 
    print("script.py: cached sig: %i " % signum) 
    sys.stdout.flush() 

    if sub is not None and not sub.poll(): 
     print("render.py: sent signal to prman pid: ", sub.pid) 
     sys.stdout.flush() 
     sub.send_signal(signal.SIGTERM) 
     sub.wait() # deadlocks....???? 
     #os.kill(sub.pid, signal.SIGTERM) # this works 
     #os.waitpid(sub.pid,0)    # this works 

    for i in range(0,5): 
     time.sleep(0.1) 
     print("script.py: cleanup %i" % i) 
     sys.stdout.flush() 

    sys.exit(128+signum) 

signal.signal(signal.SIGINT, handler) 
signal.signal(signal.SIGUSR2, handler) 
signal.signal(signal.SIGTERM, handler) 

sub = subprocess.Popen(["./doStuff.sh"], stderr = subprocess.STDOUT) 
sub.wait() 


print("finished script.py") 

doStuff.sh

#!/bin/bash 

function trap_with_arg() { 
    func="$1" ; shift 
    for sig ; do 
     trap "$func $sig" "$sig" 
    done 
} 

pid=False 

function signalHandler() { 

    trap - SIGINT SIGTERM 

    echo "doStuff.sh chached sig: $1" 
    echo "doStuff.sh cleanup: wait 10s" 
    sleep 10s 

    # kill ourself to signal calling process we exited on SIGINT 
    kill -s SIGINT $$ 

} 

trap_with_arg signalHandler SIGINT SIGTERM 
trap "echo 'doStuff.sh ignore SIGUSR2'" SIGUSR2 
# ignore SIGUSR2 

echo "doStuff.sh : pid: $$" 
echo "doStuff.sh: some stub error" 1>&2 
for i in {1..100}; do 
    sleep 1s 
    echo "doStuff.sh, rendering $i" 
done 

当我将在推出的过程一个终端通过 python3 scripts.py & 一个信号与kill -USR2 -$! 脚本捕获到SIGINT,并在永远等待,ps -uf显示以下内容:

user 27515 0.0 0.0 29892 8952 pts/22 S 21:56 0:00 \_ python script.py 
user 27520 0.0 0.0  0  0 pts/22 Z 21:56 0:00  \_ [doStuff.sh] <defunct> 

请注意,doStuff.sh可以正确处理SIGINT并退出。

我还想调用handler时获得标准输出吗?如何正确地做到这一点?

非常感谢!

+0

我无法重现行为(你的操作系统,shell,python版本是什么?)。你能提供一个虚拟的'dostuff.py'作为例子吗?为什么使用' - $!'而不是'$!' - 前者可能会将信号发送给整个进程组? – jfs

+0

我发送给整个进程组,因为我在集群上运行它,它向整个进程组发送SIGUSR2信号。 – Gabriel

+0

我更新了答案,并提供了doStuff.sh。你可以在你的机器上试试这个,在我的这个死锁给出了如上所示的进程列表输出 – Gabriel

回答

1

您的代码无法获取子进程的标准输出,因为它在调用subprocess.Popen()时没有重定向其标准流。在信号处理器中做任何事情已经太迟了。

如果你想捕捉标准输出,然后通过stdout=subprocess.PIPE,并呼吁.communicate()代替.wait()

child = subprocess.Popen(command, stdout=subprocess.PIPE) 
output = child.communicate()[0] 

有一个完全独立的问题,该信号处理程序挂在.wait()呼叫的Python 3(Python的2或os.waitpid()不会在此处挂起,而是会收到错误的孩子退出状态)。下面是a minimal code example to reproduce the issue

#!/usr/bin/env python 
import signal 
import subprocess 
import sys 


def sighandler(*args): 
    child.send_signal(signal.SIGINT) 
    child.wait() # It hangs on Python 3 due to child._waitpid_lock 

signal.signal(signal.SIGUSR1, sighandler) 
child = subprocess.Popen([sys.executable, 'child.py']) 
sys.exit("From parent %d" % child.wait()) # return child's exit status 

其中child.py

#!/usr/bin/env python 
"""Called from parent.py""" 
import sys 
import time 

try: 
    while True: 
     time.sleep(1) 
except KeyboardInterrupt: # handle SIGINT 
    sys.exit('child exits on KeyboardInterrupt') 

例子:

$ python3 parent.py & 
$ kill -USR1 $! 
child exits on KeyboardInterrupt 
$ fg 
... running python3 parent.py 

的例子表明,孩子已经退出,但父母仍在运行。如果按Ctrl + C中断它;回溯显示它挂在.wait()调用中的with _self._waitpid_lock:声明中。如果self._waitpid_lock = threading.Lock()替换为 subprocess.py那么效果与使用os.waitpid()相同 - 它不挂起但退出状态不正确。

为了避免这个问题,不要等待孩子在信号处理程序中的状态:调用send_signal(),设置一个简单的布尔标志,然后从hanlder返回。在主代码中,检查child.wait()(在代码中的print("finished script.py")之前)的标志,以查看信号是否已被接收(如果从child.returncode未清楚)。如果标志已设置;调用相应的清理代码并退出。

0

你应该看看subprocess.check_output

proc_output = subprocess.check_output(commands_list, stderr=subprocess.STDOUT) 

可以围绕着它在尝试,除了然后:

except subprocess.CalledProcessError, error: 
    create_log = u"Creation Failed with return code {return_code}\n{proc_output}".format(
     return_code=error.returncode, proc_output=error.output 
    ) 
+0

''尝试: 出= subprocess.check_output([ “命令”]),除了subprocess.CalledProcessError 作为误差: 打印(error.output) )'' - >时不会异常被调用时的信号到达?我没有看到打印的字样? – Gabriel

+0

@Gabriel您必须将信号发送到您的处理程序的子进程,然后才能捕获它。 –

+0

@ Neil,感谢您的更新。我试过那个,但''sub.wait()''stucks(查看更新后的答案)。你知道如何做到这一点? – Gabriel

0

我只能等待过程中通过使用

os.kill(sub.pid, signal.SIGINT) 
    os.waitpid(sub.pid,0) 

代替

sub.send_signal(signal.SIGINT) 
    sub.wait() # blocks forever 

这事做对UNIX进程组,我真的不明白:我觉得./doStuff.sh没有接收到信号,因为在同一个进程组的孩子的不接收信号的过程。 (我不确定这是否正确)。希望有人可以详细阐述这个问题。

直到处理程序被调用的输出被推送到调用bash(控制台)的stdout。

+0

这个代码例子之间没有本质区别。 '.send_signal(sig)'在内部使用'os.kill(self.pid,sig)','.wait()'在内部使用'os.waitpid(self.pid,0)'。它与Unix上的进程组无关。 – jfs

+0

好的,所以我不明白它应该挂在那里? 也许我应该尝试一个最简单的例子 – Gabriel

+0

我的猜想是:因为在信号处理程序运行时sub.wait()持有sub._waitpid_lock锁,所以你不应该在里面调用sub.wait()处理程序 - 也许它是Python中的一个错误(RLock应该用来代替Lock)。你应该[创建一个演示问题的最小代码示例](http://stackoverflow.com/help/mcve) – jfs

相关问题