Shared memory linux python

multiprocessing.shared_memory — Provides shared memory for direct access across processes¶

New in version 3.8.

This module provides a class, SharedMemory , for the allocation and management of shared memory to be accessed by one or more processes on a multicore or symmetric multiprocessor (SMP) machine. To assist with the life-cycle management of shared memory especially across distinct processes, a BaseManager subclass, SharedMemoryManager , is also provided in the multiprocessing.managers module.

In this module, shared memory refers to “System V style” shared memory blocks (though is not necessarily implemented explicitly as such) and does not refer to “distributed shared memory”. This style of shared memory permits distinct processes to potentially read and write to a common (or shared) region of volatile memory. Processes are conventionally limited to only have access to their own process memory space but shared memory permits the sharing of data between processes, avoiding the need to instead send messages between processes containing that data. Sharing data directly via memory can provide significant performance benefits compared to sharing data via disk or socket or other communications requiring the serialization/deserialization and copying of data.

class multiprocessing.shared_memory. SharedMemory ( name = None , create = False , size = 0 ) В¶

Creates a new shared memory block or attaches to an existing shared memory block. Each shared memory block is assigned a unique name. In this way, one process can create a shared memory block with a particular name and a different process can attach to that same shared memory block using that same name.

As a resource for sharing data across processes, shared memory blocks may outlive the original process that created them. When one process no longer needs access to a shared memory block that might still be needed by other processes, the close() method should be called. When a shared memory block is no longer needed by any process, the unlink() method should be called to ensure proper cleanup.

name is the unique name for the requested shared memory, specified as a string. When creating a new shared memory block, if None (the default) is supplied for the name, a novel name will be generated.

create controls whether a new shared memory block is created ( True ) or an existing shared memory block is attached ( False ).

size specifies the requested number of bytes when creating a new shared memory block. Because some platforms choose to allocate chunks of memory based upon that platform’s memory page size, the exact size of the shared memory block may be larger or equal to the size requested. When attaching to an existing shared memory block, the size parameter is ignored.

Closes access to the shared memory from this instance. In order to ensure proper cleanup of resources, all instances should call close() once the instance is no longer needed. Note that calling close() does not cause the shared memory block itself to be destroyed.

Requests that the underlying shared memory block be destroyed. In order to ensure proper cleanup of resources, unlink() should be called once (and only once) across all processes which have need for the shared memory block. After requesting its destruction, a shared memory block may or may not be immediately destroyed and this behavior may differ across platforms. Attempts to access data inside the shared memory block after unlink() has been called may result in memory access errors. Note: the last process relinquishing its hold on a shared memory block may call unlink() and close() in either order.

A memoryview of contents of the shared memory block.

Read-only access to the unique name of the shared memory block.

Read-only access to size in bytes of the shared memory block.

The following example demonstrates low-level use of SharedMemory instances:

The following example demonstrates a practical use of the SharedMemory class with NumPy arrays, accessing the same numpy.ndarray from two distinct Python shells:

A subclass of BaseManager which can be used for the management of shared memory blocks across processes.

A call to start() on a SharedMemoryManager instance causes a new process to be started. This new process’s sole purpose is to manage the life cycle of all shared memory blocks created through it. To trigger the release of all shared memory blocks managed by that process, call shutdown() on the instance. This triggers a SharedMemory.unlink() call on all of the SharedMemory objects managed by that process and then stops the process itself. By creating SharedMemory instances through a SharedMemoryManager , we avoid the need to manually track and trigger the freeing of shared memory resources.

This class provides methods for creating and returning SharedMemory instances and for creating a list-like object ( ShareableList ) backed by shared memory.

Refer to multiprocessing.managers.BaseManager for a description of the inherited address and authkey optional input arguments and how they may be used to connect to an existing SharedMemoryManager service from other processes.

Create and return a new SharedMemory object with the specified size in bytes.

Читайте также:  Kali linux wallpaper 1920x1080

Create and return a new ShareableList object, initialized by the values from the input sequence .

The following example demonstrates the basic mechanisms of a SharedMemoryManager :

The following example depicts a potentially more convenient pattern for using SharedMemoryManager objects via the with statement to ensure that all shared memory blocks are released after they are no longer needed:

When using a SharedMemoryManager in a with statement, the shared memory blocks created using that manager are all released when the with statement’s code block finishes execution.

class multiprocessing.shared_memory. ShareableList ( sequence = None , * , name = None ) В¶

Provides a mutable list-like object where all values stored within are stored in a shared memory block. This constrains storable values to only the int , float , bool , str (less than 10M bytes each), bytes (less than 10M bytes each), and None built-in data types. It also notably differs from the built-in list type in that these lists can not change their overall length (i.e. no append, insert, etc.) and do not support the dynamic creation of new ShareableList instances via slicing.

sequence is used in populating a new ShareableList full of values. Set to None to instead attach to an already existing ShareableList by its unique shared memory name.

name is the unique name for the requested shared memory, as described in the definition for SharedMemory . When attaching to an existing ShareableList , specify its shared memory block’s unique name while leaving sequence set to None .

Returns the number of occurrences of value .

Returns first index position of value . Raises ValueError if value is not present.

Read-only attribute containing the struct packing format used by all currently stored values.

The SharedMemory instance where the values are stored.

The following example demonstrates basic use of a ShareableList instance:

The following example depicts how one, two, or many processes may access the same ShareableList by supplying the name of the shared memory block behind it:

The following examples demonstrates that ShareableList (and underlying SharedMemory ) objects can be pickled and unpickled if needed. Note, that it will still be the same shared object. This happens, because the deserialized object has the same unique name and is just attached to an existing object with the same name (if the object is still alive):

Источник

Разделяемая память для процессов на Python и не только.

Есть большое многопроцессное приложение, с рабочими процессами, всякими GUI и логгерами в отдельных процессах и т.д. Построено с использованием модуля multiprocessing. Рабочие процессы обрабатывают большие данные. Для больших данных используется multiprocessing.Array, вот так:

Синхронизация от mp.Array не требуется, процессы синхронизируются с помощью посылки/отправки сообщений через p1/p2, поэтому lock=False.

Вопросы:
* Размер/количество буфера(ов) задаются до запуска рабочих процессов. Как правильно реализовать изменение количества/размера после того, как рабочие процессы уже стартовали? Т.е. понятно, как отправить сообщение. Непонятно как закрыть существующий буфер и открыть новый.
* Что у mp.Array под капотом? Я заметил, что python открывает много файлов с именами вида /tmp/pymp-ixc54qx7/pym-27111-h7wi_sy3. Это как-то связано с mp.Array?
* Очень желательно, чтобы к этой общей памяти можно было обращаться не только из процессов на Python. Возможно как-либо её открыть из другого стороннего процесса, написанного на чём-то ещё? Может быть мне тогда что-то другое использовать, а не mp.Array?

Аналогичные вопросы про mp.Pipe():
* Как в работающий процесс передать конец новой трубы?
* Как передать в не Python’овский процесс конец трубы?

Возможно как-либо её открыть из другого стороннего процесса, написанного на чём-то ещё?

RabbitMQ. Воркеров можно запускать на даже разных машинах и распределять задачи. Например, тяжелые обработки делать на с++ или go.

Воркеров можно запускать на даже разных машинах и распределять задачи.

Пока такой задачи нет. Те большие данные, о которых речь, обрабатывать надо на одной машине, максимально быстро.

multiprocessing внутри использует примитивы операционной системы. Ответы на твои «можно ли» и «как» зависят от ОС. Решения будут для конкретной ОС. Для разных ОС придётся писать разные решения.

Если ты хочешь туда влезть из не питоновых процессов, то тебе, возможно, действительно стоит использовать что-то готовое, что умеет разные языки программирования и ОС.

Я пришёл к выводу что multiprocessing непригоден для написания реального кода и лучше держаться от него подальше. fork + pipe/unix socket и все вопросы взаимодействия с не-питоном и передачи в другие процессы отпадают. Через pipe/socket можно передавать как бинарные данные так и pickle’нутые питоновые структуры. Через unix socket также можно передавать файловые дескрипторы, но я считаю это хаком и лучше передавать пути в файловой системе.

Тоже пришёл к такому выводу. Этот multiprocessing глючен-крив, как вся моя жизнь. Начиная с того, что даже не умеет по Ctrl-C корректно завершать запущенные процессы. И это только вершина навозной кучи. То таинственные дедлоки в продакшоне по чётным числам в пятницу, то процессы молча мрут, а ошибки не обрабатываются. Типичная протекающая абстракция.

Если на одной машине из разных процессов то, shared memory. А вообще большие данные и максимально быстро, это не про питон.

Я пришёл к выводу что multiprocessing непригоден для написания реального кода

Почему непригоден? И, если дочерние процессы создавать вручную, то не возникнет ли проблем с отладкой? Сейчас, используя PyCharm, я могу ставить breakpoint’ы в разных процессах и отладчик их ловит. Если отказаться от mp, как отлаживать?

Через pipe/socket можно передавать как бинарные данные так и pickle’нутые питоновые структуры

На данный момент mp.Pipe удобен тем, что сам делает и pickle/unpickle и чтение из такой «трубы» просто возвращает объект. При ручной работе с pipe/socket надо будет самому велосепедировать pickle/unpickle или есть что-то готовое?

Читайте также:  Mac os как отключить микрофон

А что в этом плохого?

И чем его заменить? Просто запускать отдельные процессы и общаться сними через pipe/socket?

А какой именно его вариант лучше?

Ответы на твои «можно ли» и «как» зависят от ОС

Linux, конечно. Это же не винфак.

что-то готовое, что умеет разные языки программирования

Такой задачи нет.

mp.Array внутри использует разделяемую память, точнее говоря файл отображенные в память в shared-режиме. Это работает примерно одинаково во всех операционках (с поправкой на то что windows не позволяет штатно увеличивать размер на ходу, и совсем не позволяет го уменьшать).

Я совсем не знаток питоновских библиотек, но думаю точно есть что-то готовое чтобы класть некие данные в файл и шарить между процессами.

Если обрабатываемые данные можно представить в виде key-value (где value может быть до гигабайта размером), то возможно подойдет libmdbx. Грубо говоря, это шустрый key-value в разделяемой памяти с ACID. Но пока у меня нет байдингов для питона (приму pull-request и т.п.).

В смысле? Он один, доступен через api ядра. Тебе ведь надо общий доступ из разных процессов, например на java или C++? Ищи биндинги на питоне к mmap.

Мне почему-то казалось, что есть несколько вариантов, как минимум sysv и posix.

Я уже не вспомню конкретику. У меня была модель мастер→процессы-обработчики→обратно в мастер, и я намучился просто феерически, потому что примитивы mp не умели базовые вещи для этого необходимые. То прерывание работы нормально не обработать, то конец очереди не отловить, то не заблокироваться когда очередь переполнена, то не уничтожить нормально непустую очередь из которой никто уже не читает, то дедлок в мастере, который очень сложно обходится, что-то из этого класса проблем, разнообразное и обильное.

И, если дочерние процессы создавать вручную, то не возникнет ли проблем с отладкой?

Я не пользуюсь отладкой, так что не знаю. Попробуйте, там нет никакой магии — mp на том же форке и пайпах и построен.

На данный момент mp.Pipe удобен тем, что сам делает и pickle/unpickle и чтение из такой «трубы» просто возвращает объект. При ручной работе с pipe/socket надо будет самому велосепедировать pickle/unpickle или есть что-то готовое?

Вам же нужно взаимодействие с не-питон процессами, какой там пиклинг? А так, это либо обёртка на 10 строчек, или добавление pickle()/unpickle() в write/read. Ну либо конкретно здесь и mp.Pipe сгодится. Без остального mp.

Как минимум, непереносимо в целом и привязывает к сокетам не unix, когда вполне хватило бы пайпов. Есть подводные камни, что-то там в man было про передачу закрытого дескриптора. Для высокоуровневых языков нужны дополнительные телодвижения для развёртывания высокоуровневого примитива для вытаскивания дескриптора на одной стороне и обратного процесса на другой. В конце концов, файловые дескрипторы это приватный ресурс процесса, и любые попытки их шаринга и передачи — нарушение инкапсуляции, усложнение архитектуры и сад граблей.

Это opnesource.ru. Кто тебя знает, может ты под иллюмосом запускать будешь.

Да кто ж тебя знает-то?

Для «больших данных» всякие хадупы используют, но ты до них явно не дорос если тебе multiprocessing хватало.

Используй mmap с MAP_SHARED и не парь мозг.

sysv вроде он считается устаревшим и менее развитым.

Есть модуль, так и называется mmap. Единственно что непонятно, это как сделать mmap без записи файла и взаимодействия с ФС. mmap (из модуля mmap) первым параметром принимает файловый дескриптор. Дескриптор может быть -1, тогда файл не создаётся, но и имени у него не будет. Как тогда поделиться этим участком памяти с другими процессами? В POSIX же есть shm_open, которая возвращает дескриптор по имени (как обычный open), но, насколько я понимаю, запись на диск при этом не производится.

Для больших данных надо Go/C использовать. Python и многопоточность — это тупо костыли, и так. «чтобы было», не далеко от Си ушло, несмотря на то, что, казалось бы, Питон. Я бы из Python ставил задачу в очередь, и обрабатывал бы на Go или Сишке. Первое предпочтительное. Но на сишке можно нативный python module написать 🙂

Так что Python — RQ/Nats/Celery/ZeroMQ — Go engine

Дескриптор может быть -1, тогда файл не создаётся, но и имени у него не будет. Как тогда поделиться этим участком памяти с другими процессами?

Только через fork.

В POSIX же есть shm_open, которая возвращает дескриптор по имени (как обычный open), но, насколько я понимаю, запись на диск при этом не производится.

Да, но… В зависимости от реализации, но обычно создается файл где-нибудь в /tmp или /dev/shmю

Читайте также:  Start windows normal 11y

На Linux можно смело использовать /dev/shm, т.е. создать там файл и юзать дескриптор для mmap.

Это не подходит. Что делать, когда нужно создать новый буфер после запуска процессов рабочих?

На Linux можно смело использовать /dev/shm

Тоже такая мысль пришла, но что-то ничего не нагуглил, как корректно это делать. Можно выбрать любое имя? Нужно вручную очищать перед завершением последнего процесса или как только не будет ни одного процесса, память будет освобождена?

я по питону не специалист, но в доке на модуль mmap есть параметр tagname.

tagname, if specified and not None, is a string giving a tag name for the mapping. Windows allows you to have many different mappings against the same file. If you specify the name of an existing tag, that tag is opened, otherwise a new tag of this name is created. If this parameter is omitted or None, the mapping is created without a name. Avoiding the use of the tag parameter will assist in keeping your code portable between Unix and Windows.

Как то мутно это написано.

Тоже такая мысль пришла, но что-то ничего не нагуглил, как корректно это делать. Можно выбрать любое имя?

Нужно вручную очищать перед завершением последнего процесса или как только не будет ни одного процесса, память будет освобождена?

Нужно unlink, можно сразу после того, как все процессы открыли файл.

Ага, я тоже не понял. Это какое-то вендоспецифическое. И в (Unix version) ничего такого не предлагается.

Да это для винды.

Это не подходит. Что делать, когда нужно создать новый буфер после запуска процессов рабочих?

Если без fork() от основного процесса, то только через создание файлов. В конечном счете shm_open() делает ровно тоже самое.

Ну имя должно быть либо уникальное и заведомо известно, либо http://man7.org/linux/man-pages/man3/mkstemp.3.html. Но при уникальном авто-генерируемом имени его нужно как-то сообщить каждому процессу в «оркестре».

Нужно вручную очищать перед завершением последнего процесса или как только не будет ни одного процесса, память будет освобождена?

В /dev/shm на актуальных linux-ах смонтирована tmpfs, и всё что там лежит будет «жить» в ОЗУ пока к inode есть ссылки:

  1. когда файл есть в какой-то директории.
  2. пока есть хотя-бы один процесс, в котором этот файл открыт или отображен в память.

Т.е. файл нужно удалить когда он станет ненужным, иначе будет просто занимать ОЗУ до перезагрузки. При этом можно удалить как только все процессы из «оркестра» открыли файл и/или отобразили в память.

Однако, как только файл будет удален «просто так» к его содержимому уже не подключишься (только открывая соответствующий дескриптор в /proc/$PID/fd/

Дескриптор может быть -1, тогда файл не создаётся, но и имени у него не будет. Как тогда поделиться этим участком памяти с другими процессами?

Сделать дескриптор через memfd_create, делиться с другими процессами через /proc/

Ага, я тоже не понял. Это какое-то вендоспецифическое. И в (Unix version) ничего такого не предлагается.

В linux это реализуется созданием файла вида /dev/shm/LIKE_WINDOWS_NAMED_SHARED_MEMORY_$NAME.

Внутри Windows это реализовано примерно также, но имена этих файлов видны через Native API, а не всем. Важное отличие в том, что Windows самостоятельно удаляет такой файл с закрытием последнего дескриптора.

В linux же такое «авто-удаление» нужно реализовывать отслеживая закрытие дескрипторов, в том числе начиная танцевать с O_TMPFILE-бубном.

Сделать дескриптор через memfd_create, делиться с другими процессами через /proc//fd/.

Кстати да, про относительно новый memfd_create() я забыл. Удобство в том, что имя явно видно в /proc/$PID/fd/

Но только это почти тоже самое, как если начать с fork().

Но только это почти тоже самое, как если начать с fork().

Да ну нет. Созданный через memfd_create файл ты можешь открыть в паралельном процессе, о чем явно сказано в манах.

А профит ещё в том, что не надо париться с уникальным именем и руками освобождать память.

Недостаток — динамически генерированые пути, которые на глаз не отличаются от путей других процессов.

Да ну нет. Созданный через memfd_create файл ты можешь открыть в паралельном процессе, о чем явно сказано в манах.

А профит ещё в том, что не надо париться с уникальным именем и руками освобождать память.

Ок, уговорил что лучше.

Но я имел в виду, что запуск нужно начинать с какого-то первого/главного процесса. А тогда можно сделать еще так = в этом процессе создать файлик c O_TMPFILE, сдвинуть его дескриптор посредством dup2() на какой-нибудь 142й номер, и потом открыть во всех потомках.

У O_TMPFILE есть особенность, что файл создаётся в файловой системе в указаной в open директории. Если нужно, чтобы файл лежал в оперативной памяти, то надо найти и указать в open директорию, лежащаю в tmpfs. Иначе будет писать на диск.

только открывая соответствующий дескриптор в /proc/$PID/fd/

Хм. Это можно сделать из стороннего процесса?

В конечном счете shm_open() делает ровно тоже самое.

Разве он что-то создаёт на уровне ФС? Мне казалось, там своё пространство имён.

Хм. Это можно сделать из стороннего процесса?

Разве он что-то создаёт на уровне ФС? Мне казалось, там своё пространство имён.

Источник

Оцените статью