【问题标题】:subprocess popen stdout locking up?子进程弹出标准输出锁定?
【发布时间】:2012-04-26 07:24:28
【问题描述】:

您好,使用 subprocess.Popen 时读取标准输出有问题

daniel@desktop:~$ python -V
Python 2.7.3

代码如下:(注释代码是我尝试过的一些东西)

import subprocess

RUN = './hlds_run -game cstrike -maxplayers 11'

p = subprocess.Popen(RUN.split(), shell=False, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE)

while 1:
    try:
        out = p.stdout.readline()
        #if out == '':
        #   out = p.stdout.read()
        #p.stdout.flush()
    except: 
        p.terminate()
        break

    try:
        err = p.stderr.readline()
        #if err == '':
        #   err = p.stderr.read()
        #p.stderr.flush()
    except:
        p.terminate()
        break
    if out != '':
        print out.rstrip()

    if err != '':
        print err.rstrip()


    #print '\n' #constantly prints new lines until "Calling BreakpadMiniDumpSystemInit."

这是连接到服务器并断开连接时得到的输出:

daniel@desktop:~/hlds$ python hlds.py 
Auto detecting CPU
Using breakpad crash handler
Using Pentium II Optimised binary.
Setting breakpad minidump AppID = 10
Auto-restarting the server on crash
Forcing breakpad minidump interfaces to load

Looking up breakpad interfaces from steamclient
Console initialized.
Calling BreakpadMiniDumpSystemInit
scandir failed:/home/daniel/hlds/./valve/SAVE
Installing breakpad exception handler for appid(10)/version(5447)
scandir failed:/home/daniel/hlds/./platform/SAVE
Looking up breakpad interfaces from steamclient
Protocol version 48
Calling BreakpadMiniDumpSystemInit

while 循环在之后锁定:

Calling BreakpadMiniDumpSystemInit.

但服务器仍在运行,我可以连接、运行命令等...


如果我运行:

 ./hlds_run -game cstrike -maxplayers 11 >> stdout.log 2>&1

我在 stdout.log 中得到以下输出:

daniel@desktop:~/hlds$ cat stdout.log 
Auto detecting CPU
Using Pentium II Optimised binary.
Auto-restarting the server on crash

Console initialized.
Using breakpad crash handler
Setting breakpad minidump AppID = 10
Forcing breakpad minidump interfaces to load
Looking up breakpad interfaces from steamclient
Calling BreakpadMiniDumpSystemInit
Installing breakpad exception handler for appid(10)/version(5447)
scandir failed:/home/daniel/hlds/./valve/SAVE
scandir failed:/home/daniel/hlds/./platform/SAVE
Protocol version 48
Exe version 1.1.2.6/Stdio (cstrike)
Exe build: 14:06:24 Sep 23 2011 (5447)
STEAM Auth Server
Server IP address 127.0.1.1:27015
couldn't exec listip.cfg
couldn't exec banned.cfg
Looking up breakpad interfaces from steamclient
Calling BreakpadMiniDumpSystemInit
scandir failed:/home/daniel/hlds/./valve/SAVE
scandir failed:/home/daniel/hlds/./platform/SAVE

Could not establish connection to Steam servers.
Reconnected to Steam servers.
   VAC secure mode is activated.
ERROR: couldn't open custom.hpk.
JAMES HETFIELD : hello!
Dropped JAMES HETFIELD from server
Reason:  Client sent 'drop'
Sat Apr 14 00:10:54 CEST 2012: Server Quit

但是,如果我不执行 2>&1,我仍然会在 stdout 中获得此输出,而其余的则在 stdout.log 中:

daniel@desktop:~/hlds$ ./hlds_run -game cstrike -maxplayers 11 >> stdout.log
Using breakpad crash handler
Setting breakpad minidump AppID = 10
Forcing breakpad minidump interfaces to load
Looking up breakpad interfaces from steamclient
Calling BreakpadMiniDumpSystemInit
Installing breakpad exception handler for appid(10)/version(5447)
Looking up breakpad interfaces from steamclient
Calling BreakpadMiniDumpSystemInit

尝试创建 servermanager 和 dispatcher 作为子流程学习体验:]

感谢所有帮助! :)

【问题讨论】:

    标签: python subprocess python-2.7


    【解决方案1】:

    发生的情况是其中一个管道被数据填满,从而阻塞了子进程,而您的 python 进程被阻塞试图从另一个管道读取一行,从而导致死锁。您可能想要使用某种轮询(select/poll/epoll)而不是在管道上进行阻塞读取。

    一个快速的技巧是在你的 while 循环中进行非阻塞读取,但这会导致你的 python 进程使用大量 CPU。

    查看select 模块的文档,了解有关以非骇客方式解决问题的更多信息。

    【讨论】:

    • 绝对是一个正确的答案,因为subprocess 模块在内部使用select(或在Windows 上使用threading)。但是,OP 可能会发现 pexpect 更适合这种事情。
    • 确实如此。 pexpect 是 OP 想要解决的问题的更好解决方案。但是,由于 OP 试图将问题作为一种学习体验来解决,我认为使用 select 是适用的。
    【解决方案2】:

    您是否能够使用 select 解决此问题?我发现当我启动 SRCDS 时,即使是非阻塞的,它也不会超过“调用 BreakpadMiniDumpSystemInit”:

    p = subprocess.Popen(cmd, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True)
    fcntl.fcntl(p.stdout, fcntl.F_SETFL, fcntl.fcntl(p.stdout, fcntl.F_GETFL) | os.O_NONBLOCK)
    print p.stdout.read()
    

    这会打印到“BreakpadMiniDumpSystemInit”消息,进一步调用 read() 会抛出“资源暂时不可用”,直到将某些内容写入 p.stdin。

    【讨论】:

    • 是的,我发现它工作得很好,我从本教程doughellmann.com/PyMOTW/select 中学到了所有我必须知道的知识,并根据我的需要对其进行了修改。在您的情况下,似乎有东西阻塞了标准输出管道。
    猜你喜欢
    • 2018-03-08
    • 2013-06-02
    • 2019-02-24
    • 2010-10-17
    • 2011-08-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-29
    相关资源
    最近更新 更多