Почему Linux может принимать сокеты в многопроцессорности?

Этот код отлично работает на 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?

Ответ 1

На платформах unix сокеты и другие файловые дескрипторы могут быть отправлены в другой процесс с использованием сокетов unix domain (AF_UNIX), поэтому сокеты можно травить в контексте многопроцессорности.

В модуле многопроцессорности используется специальный экземпляр pickler вместо обычного pickler, ForkingPickler, чтобы распиливать сокеты и дескрипторы файлов, которые затем могут быть разбросаны в другом процессе. Это можно сделать только потому, что известно, что маринованный экземпляр будет рассыпаться, не имеет смысла рассортировать сокет или файловый дескриптор и отправлять его между границами машины.

Для окон есть аналогичные механизмы для открытых файлов.

Ответ 2

Я думаю, что проблема заключается в том, что multiprocessing использует другой pickler для Windows и не-Windows-систем. В Windows нет реального fork(), а травление, которое выполняется, эквивалентно травлению через границы машины (т.е. Распределенные вычисления). В системах, отличных от Windows, объекты (например, дескрипторы файлов) могут быть разделены между границами процесса. Таким образом, травление в системах Windows (с pickle) является более ограниченным.

В пакете multiprocessing используется copy_reg для регистрации нескольких типов объектов до pickle, а один из этих типов - socket. Однако сериализация объекта socket, используемого в Windows, более ограничена из-за слабой разборчивости Windows.

В соответствующей заметке, если вы хотите отправить объект socket с multiprocessing в Windows, вы можете... вам просто нужно использовать пакет multiprocess, который использует dill вместо pickle, dill имеет лучший сериализатор, который может выделять объекты socket на любую ОС, и поэтому отправка объекта socket с помощью multiprocess работает в любом случае.

dill имеет функцию copy; по существу loads(dumps(object)) - который полезен для проверки объекта, может быть сериализован. dill также имеет check, который выполняет copy, но с более ограничительной операцией типа "Windows". Это позволяет пользователям в системах, отличных от Windows, эмулировать copy в системе Windows или через распределенные ресурсы.

>>> 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, отличаясь от сортировщика, который он использует в системах, отличных от Windows. Тем не менее, возможно (и легко) работать с любой ОС, используя лучший сериализатор (как используется в multiprocess).