【问题标题】:Why can Linux accept sockets in multiprocessing?为什么 Linux 可以在多处理中接受套接字?
【发布时间】:2017-04-19 14:15:21
【问题描述】:

此代码在 Linux 上运行良好,但在 Windows 下失败(这是预期的)。我知道多处理模块使用fork() 来生成一个新进程,因此父级(即打开的套接字)拥有的文件描述符由子级继承。但是,据我了解,您可以通过多处理发送的唯一数据类型需要是可腌制的。在 Windows 和 Linux 上,套接字对象是不可腌制的。

from socket import socket, AF_INET, SOCK_STREAM
import multiprocessing as mp
import pickle

sock = socket(AF_INET, SOCK_STREAM)
sock.connect(("www.python.org", 80))
sock.sendall(b"GET / HTTP/1.1\r\nHost: www.python.org\r\n\r\n")

try:
    pickle.dumps(sock)
except TypeError:
    print("sock is not pickleable")

def foo(obj):
    print("Received: {}".format(type(obj)))
    data, done = [], False
    while not done:
        tmp = obj.recv(1024)
        done = len(tmp) < 1024
        data.append(tmp)
    data = b"".join(data)
    print(data.decode())


proc = mp.Process(target=foo, args=(sock,))
proc.start()
proc.join()

我的问题是为什么 socket 对象,一个明显不可腌制的对象,可以通过多处理传递?它不像Windows那样使用pickle吗?

【问题讨论】:

  • 文件描述符没有“发送”,它们就在那里。
  • 什么意思?我可能(并且必须部分)错了,但我认为“args”参数中的任何内容都需要是可腌制的。不是这样吗?
  • 参数没有被腌制,它们只是传递给子进程中的函数。只有进程之间传输的对象需要被pickle。
  • 谁来决定那是什么?操作系统大概。那么实际的socket对象本身并不是在进程之间发送的,而是维护了底层的文件描述符呢?我知道它是由孩子继承的,但我认为套接字对象也传递给了孩子,而不仅仅是简单的描述符。
  • 不,套接字对象与进程内存映射的所有其他部分一样,已经存在在子进程中,因为分叉会产生几乎精确的父母的副本。

标签: python linux sockets python-multiprocessing


【解决方案1】:

我认为问题在于multiprocessing 对 Windows 和非 Windows 系统使用了不同的pickler。在 Windows 上,没有真正的fork(),所做的酸洗相当于跨机器边界酸洗(即分布式计算)。在非 Windows 系统上,对象(如文件描述符)可以跨进程边界共享。因此,Windows 系统(使用pickle)上的酸洗受到更多限制。

multiprocessing 包确实使用copy_reg 将一些对象类型注册到pickle,其中一种类型是socket。但是,在 Windows 上使用的 socket 对象的序列化受到更多限制,因为 Windows pickler 较弱。

在相关说明中,如果您确实想在 Windows 上发送带有multiprocessingsocket 对象,您可以……您只需使用包multiprocess,它使用dill 而不是pickle . dill 有一个更好的序列化程序,可以在任何操作系统上腌制socket 对象,因此在任何一种情况下都可以发送带有multiprocesssocket 对象。

dill具有copy的功能;本质上是loads(dumps(object))——这对于检查对象是否可以序列化很有用。 dill 也有 check,它执行 copy,但具有更多限制性的“Windows”风格的类似 fork 的操作。这允许非 Windows 系统上的用户在 Windows 系统上或跨分布式资源模拟 copy

>>> import dill
>>> import socket
>>> s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
>>> s.connect(('www.python.org', 80))
>>> s.sendall(b'GET / HTTP/1.1\rnHost: www.python.org\r\n\r\n')
>>> 
>>> dill.copy(s)
<socket._socketobject object at 0x10e55b9f0>
>>> dill.check(s)
<socket._socketobject object at 0x1059628a0>
>>> 

简而言之,这种差异是由于multiprocessing 在 Windows 上使用的pickler 与它在非Windows 系统上使用的pickler 不同造成的。但是,通过使用更好的序列化程序(如multiprocess 中所使用的),可以(并且很容易)在任何操作系统上工作。

【讨论】:

  • 全面披露:我是multiprocessdill 的作者。
  • 非常有趣的模块,莳萝!
【解决方案2】:

在 unix 平台上,套接字和其他文件描述符可以使用 unix 域 (AF_UNIX) 套接字发送到不同的进程,因此套接字可以在多处理的上下文中被腌制.

多处理模块使用特殊的pickler 实例而不是常规的pickler,ForkingPickler,来pickle 套接字和文件描述符,然后可以在不同的进程中取消pickle。之所以可以这样做,是因为知道腌制实例将在何处取消腌制,腌制套接字或文件描述符并在机器边界之间发送它是没有意义的。

对于 Windows,有 similar mechanisms 用于打开文件句柄。

【讨论】:

    猜你喜欢
    • 2010-12-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-21
    • 2020-08-18
    • 2016-06-16
    相关资源
    最近更新 更多