Почему 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
).