如何在不使用communication()的情况下避免子进程中的死锁

时间:2015-11-24 05:56:27

标签: python subprocess stdout deadlock popen

proc = subprocess.Popen(['start'],stdin=subprocess.PIPE,stdout=subprocess.PIPE)
proc.stdin.write('issue commands')
proc.stdin.write('issue more commands')
output = proc.stdout.read()   # Deadlocked here

# Actually I have more commands to issue here 

我知道communication()可以为我提供这个问题的解决方案,但我想稍后发出更多命令。但是communication()有点关闭子进程。

这里有什么知道的工作吗?我正在尝试使用python包装器与路由器进行交互。所以我有更多的命令出现,也可以产生一些输出。在那种情况下,如何在不终止子进程的情况下进行读取。

1 个答案:

答案 0 :(得分:10)

output = proc.stdout.read()   # Deadlocked here

该行导致死锁,因为read()在读取EOF之前不会返回,EOF在另一方关闭其stdout时发送(例如,当子进程终止时)。相反,您想要读取面向行的输入:

output = proc.stdout.readline()
在读取换行符(或EOF)后,即在readline()读取之后,

readline()将返回。

您的下一次死锁将来自:

  1. 当子进程尝试读取面向行的输入时,不会向发送到子进程的输出添加newline,即子进程在从stdin读取时查找换行符。

  2. 输出不是flushing,这意味着对方永远不会看到任何要读取的数据,因此另一方会挂起等待数据。

  3. 为了提高效率,当你写入一个文件(包括stdout,stdin)输出缓冲时,这意味着python不是将输出实际写入文件,而是将输出存储在列表(称为缓冲区)。然后当列表增长到一定大小时,python会立即将所有输出写入文件,这比一次写一行更有效。

    不能为发送到子流程的所有输出添加换行符,可以很容易地更正;但是,发现所有需要刷新缓冲区的地方更加困难。这是一个例子:

    prog.py:

    #!/usr/bin/env python3.4 -u
    
    import sys
    
    print('What is your name?') 
    name = input()
    print(name)
    
    print("What is your number?")
    number = input()
    print(number)
    

    假设您想用其他程序驱动该程序?

    1. 制作prog.py可执行文件:$ chmod a+x prog.py

    2. 注意shebang线。

    3. 请注意shebang行中python解释器的-u flag,这意味着的所有输出程序都将是无缓冲的,即它将直接写入{{ 1}} - 没有累积在缓冲区中。

    4. stdout

      对评论的回应:

      你可能遭受缓冲。大多数程序缓冲输出以提高效率此外,一些程序将尝试确定它们的stdout是否连接到终端 - 如果是,则程序使用行缓冲,这意味着从缓冲区检索输出并实际写入stdout每次程序输出换行符。这允许使用连接终端的人与程序进行交互。

      另一方面,如果程序的stdout没有连接到终端,程序将阻塞缓冲区,这意味着从缓冲区中检索输出,并且只有在缓冲区有缓冲区之后才会将其写入stdout增长到特定大小,比如4K数据。如果程序是块缓冲并且输出小于4K,则实际上没有任何内容写入stdout。块缓冲允许其他计算机程序以更高的效率检索程序的输出。

      如果路由器程序是块缓冲,并且输出的块大小小于块大小,那么实际上没有任何内容写入stdout,因此你的python程序没有任何东西可以读取。

      您的python程序不是终端,因此路由器程序可能是块缓冲。正如JF Sebastian在评论中指出的那样,有办法欺骗路由器程序认为你的python程序是一个终端,这会导致路由器程序行缓冲区,因此你将能够用import subprocess as sp child = sp.Popen( ['./prog.py'], stdin = sp.PIPE, stdout = sp.PIPE ) print_question = child.stdout.readline().decode('utf-8') #PIPE's send a bytes type, #so convert to str type name = input(print_question) name = "{}\n".format(name) child.stdin.write( name.encode('utf-8') #convert to bytes type ) child.stdin.flush() print_name = child.stdout.readline().decode('utf-8') print("From client: {}".format(name)) print_question = child.stdout.readline().decode('utf-8') number = input(print_question) number = "{}\n".format(number) child.stdin.write( number.encode('utf-8') ) child.stdin.flush() print_number = child.stdout.readline().decode('utf-8') print("From client: {}".format(print_number)) 从stdout读取。看看pexpect。