V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
TigerS
V2EX  ›  Python

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

  •  
  •   TigerS · 2016-03-15 01:37:14 +08:00 · 10471 次点击
    这是一个创建于 3211 天前的主题,其中的信息可能已经有所发展或是发生改变。

    现在有一个程序,除非 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) 都不行

    23 条回复    2016-03-18 23:17:01 +08:00
    xiamx
        1
    xiamx  
       2016-03-15 01:56:21 +08:00
    Control-C 发的是 SIGHUP
    TigerS
        2
    TigerS  
    OP
       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
        3
    firstway  
       2016-03-15 07:00:54 +08:00 via Android
    go 程序是谁写的?“这个 Go 程序的运行是除非主动停止否则不会停止。”说明这个 go 重载信号处理函数,或者 go 默认一下处理函数。如果你能改可以去改这些函数,甚至可以选另外的信号添加处理函数。
    如果你不能修改,可以发信号 9 。这个 go 退出可能不太优雅。
    firstway
        4
    firstway  
       2016-03-15 07:05:23 +08:00 via Android
    还有一个可能性,就是 go 可能使用 fork 一次或多次,父已经退出,你根本没进程去 kill 。这个可以通过 proc 打印 pid 和 ps 进行对照。
    chinuno
        5
    chinuno  
       2016-03-15 07:51:49 +08:00
    脚本里面调用 killall 应该可以解决了
    dorentus
        6
    dorentus  
       2016-03-15 08:05:09 +08:00
    @xiamx 是 SIGINT 吧
    auser
        7
    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
        8
    xiamx  
       2016-03-15 08:16:59 +08:00
    @dorentus 恩对,我记错了
    knightdf
        9
    knightdf  
       2016-03-15 09:30:03 +08:00
    关机
    clino
        10
    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
        11
    mengzhuo  
       2016-03-15 11:09:26 +08:00
    哈哈哈,楼上的都没考虑到一下情况。
    用 Go 写的程序不会主动退出的原因:程序本就不应该自己退出,但是由于种种原因,楼主没有要到源代码,或者没能力改,所以没办法进行二次修改。
    但是楼主只是想要的标准输出日志进行分析呢?
    TigerS
        12
    TigerS  
    OP
       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
        13
    chinuno  
       2016-03-15 12:35:30 +08:00
    @TigerS 怎么会呢,判断一下当前系统, Linux 的话用 pkill 或者 killall , Windows 用 taskkill ,打开的 go 程序是另外的进程不至于 Python 解释器也一起关掉吧
    Jblue
        14
    Jblue  
       2016-03-15 12:42:54 +08:00
    win 下 kill ,用 taskkill 。
    timonwong
        15
    timonwong  
       2016-03-15 12:56:01 +08:00   ❤️ 2
    其实问题在这里
    communicate() 会等待程序退出之后再返回,所以你不要调用 communicate
    需要拿结果,写代码取 stdout 和 stderr ,等取完后,写代码结束这个 go 进程
    julyclyde
        16
    julyclyde  
       2016-03-15 15:19:00 +08:00
    @xiamx ctrl+C 显然不是 HUP 啊
    limbo0
        17
    limbo0  
       2016-03-15 16:04:48 +08:00
    os._exit(0)
    neoblackcap
        18
    neoblackcap  
       2016-03-15 17:25:18 +08:00
    赞成 @timonwong 的回答
    communicate 方法会堵塞了主进程,而且用 communicate 来分析日志显然是错误的用法, communicate 方法返回的结果会存在内存中,很容易爆内存的。
    其实我建议是将标准输出重定向到文件,然后读文件来进行分析,最后再发送 SIGINT 来结束程序
    TigerS
        19
    TigerS  
    OP
       2016-03-15 23:44:16 +08:00
    @timonwong 非常感谢,的确是这个问题,我试一下您说的办法。
    clino
        20
    clino  
       2016-03-16 09:19:01 +08:00
    /t/126248 调用命令获取其输出我喜欢用 gevent 来做
    huangxie
        21
    huangxie  
       2016-03-16 09:45:02 +08:00
    except Exception,e:
    print "error:",e
    pass
    except KeyboardInterrupt,e:
    print "ctrl+c"
    pass
    这是之前 http://www.quzhuanpan.com 的爬虫的一部分,或许有用
    ry_wang
        22
    ry_wang  
       2016-03-17 16:07:09 +08:00
    DravenJohnson
        23
    DravenJohnson  
       2016-03-18 23:17:01 +08:00
    如果不需要和程序交涉的话,可以考虑 Popen 打开,不 PIPE 输出(除非你需要输出)然后用 win32ui 去 close window ( Only work on Windows )
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1944 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 00:50 · PVG 08:50 · LAX 16:50 · JFK 19:50
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.