简体   繁体   English

通过Python中的套接字在两个进程之间传递共享内存对象

[英]Passing shared memory object between two processes via socket in Python

My application consists of two Python processes connected by the means of a TCP socket. 我的应用程序包含两个通过TCP套接字连接的Python进程。 The first process is designed to read files and put them in shared memory rather than push down the socket, so that the other process could read them. 第一个进程旨在读取文件并将它们放入共享内存中,而不是按下套接字,以便其他进程可以读取它们。

import os
import zmq
import mmap
import time
import ctypes
import pickle
import dill
import tempfile
import multiprocessing as mp

MEETING_POINT = 'tcp://127.0.0.1:12345'
FILEFD, FILEPATH = tempfile.mkstemp()


def run_server():
    Server().run()


def read_file():
    return open(FILEPATH, 'r').read()


class Client(object):
    def __init__(self):
        self.ctx = zmq.Context()
        self.socket = self.ctx.socket(zmq.DEALER)
        self.socket.connect(MEETING_POINT)

    def cleanup(self):
        self.socket.close()
        self.ctx.term()

    def run(self):
        self.socket.send(dill.dumps(read_file()))


class Server(object):
    def __init__(self):
        self.ctx = zmq.Context()
        self.socket = self.ctx.socket(zmq.DEALER)
        self.socket.bind(MEETING_POINT)

    def cleanup(self):
        self.socket.close()
        self.ctx.term()

    def run(self):
        f = dill.loads(self.socket.recv())
        print(f)


def main():
    with open(FILEPATH, 'w') as fd:
        fd.write('blah')

    mp.Process(target=run_server).start()
    time.sleep(.5)  # a (poor, but tolerable) alternative to handshaking
    Client().run()


if __name__ == '__main__':
    main()

My question boils down to this: how do I pass information about the segment of memory, that the second process is to access, through the socket? 我的问题归结为:如何通过套接字传递有关第二个进程要访问的内存段的信息? I've tried the following (with both pickle and dill ), all to no avail: 我已经尝试了以下方法(同时使用pickledill ),但都无济于事:

  • use mmap.mmap on a file directly or to store its contents. 直接在文件上使用mmap.mmap或存储其内容。 Result: TypeError: Required argument 'fileno' (pos 1) not found when unpickling on the server side. 结果: TypeError: Required argument 'fileno' (pos 1) not found在服务器端拆箱时TypeError: Required argument 'fileno' (pos 1) not found

  • use multiprocessing.Array: 使用multiprocessing.Array:

    with open(FILEPATH, 'r') as fd: contents = bytearray(fd.read()) arr = mp.Array(ctypes.c_byte, contents, lock=False)

Result: pickle.PicklingError: Can't pickle <class 'multiprocessing.sharedctypes.c_byte_Array_4'>: it's not found as multiprocessing.sharedctypes.c_byte_Array_4 when pickling on the client side. 结果: pickle.PicklingError: Can't pickle <class 'multiprocessing.sharedctypes.c_byte_Array_4'>: it's not found as multiprocessing.sharedctypes.c_byte_Array_4在客户端进行腌制时pickle.PicklingError: Can't pickle <class 'multiprocessing.sharedctypes.c_byte_Array_4'>: it's not found as multiprocessing.sharedctypes.c_byte_Array_4

I am convinced there's a simpler (and working) way to do this, is there? 我相信有一种更简单(且可行)的方法可以做到这一点,这是吗? I can't pre-read all the necessary files and pack them into a multiprocessing.Array to pass as args when creating a server process, of course that would (supposedly) solve everything. 我无法预读所有必要的文件并将它们打包到multiprocessing.Array以在创建服务器进程时作为args传递,当然(据说)可以解决所有问题。

Answering my own question because I figured what exactly I were to do. 回答我的问题,因为我想, 究竟我是这样做。 According to man mmap , for the second process to be able to read the first one's memory it is sufficient to create a memory segment with MAP_SHARED flag. 根据man mmap说法, man mmap第二个进程能够读取第一个进程的内存,只需创建一个带有MAP_SHARED标志的内存段就足够了。 Therefore, as in mmap.mmap 's constructor this flag is set by default, all one has to do is pass the file name and not a class instance: 因此,与mmap.mmap的构造函数一样,默认情况下会设置此标志,所有要做的就是传递文件名而不是类实例:

# sometime during the first process' execution
fd = open(filepath_to_process, 'w')
memory = mmap.mmap(fd.fileno(), 0)
socket.send(filepath_to_process)

# on the other side of socket, this happens:
unprocessed_filepath = socket.recv()
fresh_fd = open(unprocessed_filepath, 'r')
mm = mmap.mmap(fresh_fd.fileno(), 0)

# voilà! All the changes are now propagated to the second process

Of course, access modes for a file and a memory region have to match, but there's a separate section in man mmap for that, called "ERRORS" . 当然,文件和内存区域的访问模式必须匹配,但是在man mmap有一个单独的部分称为“ ERRORS”

As for the shared memory, there are posix_ipc and sysv_ipc modules (the latter supports a wider variety of OSes). 至于共享内存,有posix_ipcsysv_ipc模块(后者支持更广泛的OS)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM