Python Subprocess 去跑一个永远不停止的程序的时候如何 kill 或者 stop 呢?

2016-03-15 01:37:14 +08:00
 TigerS

现在有一个程序,除非 Ctrl + C 否则不会停止,我需要一个 Python 程序去跑这个程序(需要跑的这个程序时 Go 写的)

主要代码是

cmd = '%s --config %s ' % (CORE_PROG, CONFIG_FILE_NAME)

try:

        proc = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        output = json.loads(proc.communicate()[0]) # return the command output

        if proc.returncode != 0:
            raise Exception('Testing Fail: ' + str(output))

        sleep(20)

        # Should somehow stop the proc

尝试了很多办法, proc.kill(), proc.terminate(), proc.send_signal(signal.SIGTERM) 都不行

10469 次点击
所在节点    Python
23 条回复
xiamx
2016-03-15 01:56:21 +08:00
Control-C 发的是 SIGHUP
TigerS
2016-03-15 06:22:27 +08:00
@xiamx 问题应该不是这个,在 python 中 C + C 有一个叫做 CTRL_C_EVENT 的,在 signal 下。但是这些全部都试过了,都不行。

这个是一个 python 的 test 程序,用来 test 我们用 Go 写的一个程序的运行情况,这个 Go 程序的运行是除非主动停止否则不会停止。但是如果不停止, python 就会永远运行下去,连 Outpuut 都没有。现在做法是在我们自己程序上修改一个数值让他运行 30 秒后自动停止,专门用来做 Test 这部分用,但是非常不方便,所以希望能够有一个比较好的解决办法。
firstway
2016-03-15 07:00:54 +08:00
go 程序是谁写的?“这个 Go 程序的运行是除非主动停止否则不会停止。”说明这个 go 重载信号处理函数,或者 go 默认一下处理函数。如果你能改可以去改这些函数,甚至可以选另外的信号添加处理函数。
如果你不能修改,可以发信号 9 。这个 go 退出可能不太优雅。
firstway
2016-03-15 07:05:23 +08:00
还有一个可能性,就是 go 可能使用 fork 一次或多次,父已经退出,你根本没进程去 kill 。这个可以通过 proc 打印 pid 和 ps 进行对照。
chinuno
2016-03-15 07:51:49 +08:00
脚本里面调用 killall 应该可以解决了
dorentus
2016-03-15 08:05:09 +08:00
@xiamx 是 SIGINT 吧
auser
2016-03-15 08:11:30 +08:00
def tcpdump_addr_port(addr, port):
def timeout_kill(p):
p.kill()
logging.debug("Kill tcpdump for timeout({0})".format(PCAP_TIMEOUT_SECONDS))
try:
cmdline = "/usr/sbin/tcpdump -nn -i any host {0} and port {1} -c {2}".format(addr, port, PCAP_PACKET_COUNT)
logging.debug("begin exec {0}".format(cmdline))
with open(PCAP_TMP_PATH, "w") as file:
with open("/dev/null", "w") as fnull:
p = Popen(cmdline.split(), stdout=file, stderr=fnull)
killproc = threading.Timer(PCAP_TIMEOUT_SECONDS, timeout_kill, [p])
killproc.start()
ret = p.wait()
if ret >= 0:
killproc.cancel()
logging.debug("end exec")
except Exception as e:
logging.error(u"tcpdump error {0}".format(e))
exit(-1)
xiamx
2016-03-15 08:16:59 +08:00
@dorentus 恩对,我记错了
knightdf
2016-03-15 09:30:03 +08:00
关机
clino
2016-03-15 09:52:32 +08:00
subprocess.Popen(shell_args, preexec_fn=os.setsid)
具体参考: https://blog.tonyseek.com/post/kill-the-descendants-of-subprocess/
mengzhuo
2016-03-15 11:09:26 +08:00
哈哈哈,楼上的都没考虑到一下情况。
用 Go 写的程序不会主动退出的原因:程序本就不应该自己退出,但是由于种种原因,楼主没有要到源代码,或者没能力改,所以没办法进行二次修改。
但是楼主只是想要的标准输出日志进行分析呢?
TigerS
2016-03-15 11:52:35 +08:00
@mengzhuo 输出就是标准的 stdout 输出,然后分析其中的内容,但是在 python 里面,我现有的上面的哪些代码,你不停止那个 Go , Python 就不会往下面跑... 是我哪里写错了么?

@chinuno 这个试过了,也是不行, Windows 下会直接误伤 Python 程序,最终报错

@firstway 查看任务管理器里面看到是程序还在跑,如果手动在任务管理器里面终止就正常,但是手动又达不到自动测试的效果

=====================

首先谢谢大家回复。

Go 程序是公司原有的程序,由 Team 里面另外一个人负责的,源代码是有,主体程序只是各种数据结构,逻辑和通讯用途。实际使用时候有一个 Termianl 里面的外壳程序,外壳程序调用核心代码的时候写的是当收到用户终止的信息( Ctrl+C )的时候停止,整体的情况给人的感觉就有点像在 Terminal 里面跑 python shell ,你不 Ctrl+C 就会一直在 Python Shell 里面。

另外一个我能说出来的例子有点像 Tor 的 Terminal 版本(接触到的 Termianl 软件实在太少了举不出来什么例子了)在你不终止的时候他会一直运行,只有你 Ctrl+C 的时候才会停止。

现在暂时的做法是将这个外壳包装的程序修改成 30 秒后停止,只能作为测试使用,正式生产环境下肯定是不一样的代码。

现在主要是需要测试生产环境代码,使用 python ,通过运行这个 Go 程序,达到一些测试某些数据的功能。所以需要的是在尽可能不修改 Go 原始代码时候的处理

另外说一下,程序需要跑在 Windows 下...... (晕)

楼上所说的所有办法我全部都试过了,都不能达到很好的效果,要不是 kill() 之后 Go 程序默认是返回 1 (错误状态),要么是根本终止不了。正常情况下 Ctrl+C 之后应该返回 0 的。在 Windows 下试用 CTRL_C_EVENT 直接把 python 也终止了,所以很头疼.....

还有没有什么更好的办法么??
chinuno
2016-03-15 12:35:30 +08:00
@TigerS 怎么会呢,判断一下当前系统, Linux 的话用 pkill 或者 killall , Windows 用 taskkill ,打开的 go 程序是另外的进程不至于 Python 解释器也一起关掉吧
Jblue
2016-03-15 12:42:54 +08:00
win 下 kill ,用 taskkill 。
timonwong
2016-03-15 12:56:01 +08:00
其实问题在这里
communicate() 会等待程序退出之后再返回,所以你不要调用 communicate
需要拿结果,写代码取 stdout 和 stderr ,等取完后,写代码结束这个 go 进程
julyclyde
2016-03-15 15:19:00 +08:00
@xiamx ctrl+C 显然不是 HUP 啊
limbo0
2016-03-15 16:04:48 +08:00
os._exit(0)
neoblackcap
2016-03-15 17:25:18 +08:00
赞成 @timonwong 的回答
communicate 方法会堵塞了主进程,而且用 communicate 来分析日志显然是错误的用法, communicate 方法返回的结果会存在内存中,很容易爆内存的。
其实我建议是将标准输出重定向到文件,然后读文件来进行分析,最后再发送 SIGINT 来结束程序
TigerS
2016-03-15 23:44:16 +08:00
@timonwong 非常感谢,的确是这个问题,我试一下您说的办法。
clino
2016-03-16 09:19:01 +08:00
/t/126248 调用命令获取其输出我喜欢用 gevent 来做

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/263521

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX