2016-02-17 47 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

相關問題