妖魔化服务器中的python子进程死锁

时间:2011-10-27 21:12:59

标签: python deadlock subprocess

我正在尝试为dar along these lines设置远程备份服务器。如果可能的话,我真的很想用python做所有的管道,但是我已经问过separate question了。

subprocess.Popen(cmd, shell=True)中使用netcat,我成功地进行了差异备份,就像在dar站点上的示例一样。唯一的两个问题是:

  1. 我不知道如何以这种方式动态分配端口号
  2. 如果我在后台执行服务器,则会失败。为什么?
  3. 更新:这似乎与netcat无关;即使没有netcat,它也会挂起。

    这是我的代码:

    from socket import socket, AF_INET, SOCK_STREAM
    import os, sys
    import SocketServer
    import subprocess
    
    class DarHandler(SocketServer.BaseRequestHandler):
        def handle(self):
            print('entering handler')
            data = self.request.recv(1024).strip()
            print('got: ' + data)
            if data == 'xform':
                cmd1 = 'nc -dl 41201 | dar_slave archives/remotehost | nc -l 41202'
                print(cmd1)
                cmd2 = 'nc -dl 41200 | dar_xform -s 10k - archives/diffbackup'
                print(cmd2)
                proc1 = subprocess.Popen(cmd1, shell=True)
                proc2 = subprocess.Popen(cmd2, shell=True)
                print('sending port number')
                self.request.send('41200')
                print('waiting')
                result = str(proc1.wait())
                print('nc-dar_slave-nc returned ' + result)
                result = str(proc2.wait())
                print('nc-dar_xform returned ' + result)
            else:
                result = 'bad request'
            self.request.send(result)
            print('send result, exiting handler')
    
    myaddress = ('localhost', 18010)
    def server():
        server = SocketServer.TCPServer(myaddress, DarHandler)
        print('listening')
        server.serve_forever()
    
    def client():
        sock = socket(AF_INET, SOCK_STREAM)
        print('connecting')
        sock.connect(('localhost', 18010))
        print('connected, sending request')
        sock.send('xform')
        print('waiting for response')
        port = sock.recv(1024)
        print('got: ' + port)
        try:
            os.unlink('toslave')
        except:
            pass
        os.mkfifo('toslave')
        cmd1 = 'nc -w3 localhost 41201 < toslave'
        cmd2 = 'nc -w3 localhost 41202 | dar -B config/test.dcf -A - -o toslave -c - | nc -w3 localhost ' + port
        print(cmd2)
        proc1 = subprocess.Popen(cmd1, shell=True)
        proc2 = subprocess.Popen(cmd2, shell=True)
        print('waiting')
        result2 = proc2.wait()
        result1 = proc1.wait()
        print('nc<fifo returned: ' + str(result1))
        print('nc-dar-nc returned: ' + str(result2))
        result = sock.recv(1024)
        print('received: ' + result)
        sock.close()
        print('socket closed, exiting')
    
    if __name__ == "__main__":
        if sys.argv[1].startswith('serv'):
            server()
        else:
            client()
    

    以下是服务器上发生的情况:

    $ python clientserver.py serve &
    [1] 4651
    $ listening
    entering handler
    got: xform
    nc -dl 41201 | dar_slave archives/remotehost | nc -l 41202
    nc -dl 41200 | dar_xform -s 10k - archives/diffbackup
    sending port number
    waiting
    
    [1]+  Stopped                 python clientserver.py serve
    

    以下是客户端发生的事情:

    $ python clientserver.py client
    connecting
    connected, sending request
    waiting for response
    got: 41200
    nc -w3 localhost 41202 | dar -B config/test.dcf -A - -o toslave -c - | nc -w3 localhost 41200
    waiting
    FATAL error, aborting operation
    Corrupted data read on pipe
    nc<fifo returned: 1
    nc-dar-nc returned: 1
    

    客户端也会挂起,我必须用键盘中断来杀死它。

2 个答案:

答案 0 :(得分:1)

我减少了损失并重新开始。这种解决方案尝试非常复杂和困难。该地区有许多现成的解决方案:

如果你想采用简单的路线,rsync + ssh用于硬核,那么Fwbackup听起来不错。

答案 1 :(得分:1)

  1. 使用Popen.communicate()代替Popen.wait()

    python documentation for wait()州:

    警告:如果子进程为stdout或stderr管道生成足够的输出,使其阻塞等待OS管道缓冲区接受更多数据,则会发生死锁。使用communic()来避免这种情况。

  2. Dar及其相关的可执行文件should get a -Q,如果它们没有以交互方式运行。

  3. 同步多个流程时,请务必先在“最薄弱的链接”上调用communicate()dar_slave前的dar_xformdar之前的cat }。这是在问题中正确完成的,但值得注意。

  4. 清理共享资源。客户端进程正在打开一个dar_xform仍在读取的套接字。尝试在 dar之后在初始套接字上发送/ recv数据并且朋友完成而不关闭该套接字将因此导致死锁。

  5. Here is a working example,它不使用shell=True或netcat。这样做的一个优点是我可以动态分配辅助端口,因此可以设想同时为多个备份客户端提供服务。