I’ve wondered why isn’t it easier to have multiple processes be able to have a shared memory space.
I’ve also wondered about how to pass shared memory definitions between processes.
Examples
import multiprocessing
from contextlib import suppress
from concurrent.futures import ProcessPoolExecutor, as_completed
from shmutils import MappedMemory, MapFlags, MapProtections
from shmutils.utils import cffiwrapper
from shmutils.lock import Lock
from shmutils.shm import shm_open, shm_unlink
def _increment_to(m, lock: Lock, value: cffiwrapper, limit: int):
"""
Returns the number of times we increments the number
"""
count = value[0]
num_incr = 0
while count < limit:
with lock:
count = value[0]
if count >= limit:
break
num_incr += 1
value[0] = count + 1
return num_incr
if __name__ == "__main__":
with suppress(FileNotFoundError):
shm_unlink("test-lock")
fd = shm_open("test-lock", "w+")
size = 4096 * 100
fd.truncate(size)
assert fd.size() == size
with MappedMemory(0, size, MapProtections.READ_WRITE, MapFlags.SHARED, fd) as m:
with ProcessPoolExecutor(mp_context=multiprocessing.get_context("fork")) as exe:
lock = Lock(m)
counter = m.new("size_t *", 0)
with lock:
result1 = exe.submit(_increment_to, m, lock, cffiwrapper(counter, m), 2_0000)
result2 = exe.submit(_increment_to, m, lock, cffiwrapper(counter, m), 2_0000)
tuple(as_completed((result1, result2)))
r1, r2 = result1.result(), result2.result()
assert (result1.exception(), result2.exception()) == (None, None)
assert counter[0] == 2_0000
assert r1 + r2 == 2_0000
A MappedMemory is helpful for straddling the relative/absolute address world.
Roadmap
✅ Virtual address stability
MappedMemory.at (relative address to bytes)
MappedMemory.absolute_at (absolute address to bytes)
MappedMemory.abs_address_at (relative address -> absolute address)
MappedMemory.relative_address_at (absolute address -> relative address)
🚧 Support Python multiprocessing.get_context("spawn")
☐ figure out pickling of cffi.CData
☐ switch to instruct for internal classes
implement something like:
from instruct import CBase, class_metadata, astuple from shmutils import MemoryMap, MemoryFlags, ffi as shmffi ... fd = shm_open(...).truncate(PAGE_SIZE) page = MemoryMap(None, PAGE_SIZE, flags=MemoryFlags.SHARED, fd=fd) ... ffi = cffi.FFI() # You can include other ffi's to reuse c type declarations ffi.include(shmffi) ffi.cdef(''' typedef enum {INACTIVE, ACTIVE, DELETED} OrgUserStatus_t; ''') # pass into the instruct.CBase class an ffi instead of ``instruct.default_ffi`` class User(CBase, ffi=ffi): __slots__ = ''' struct org_user_t { uint64_t id; char *fullname; uint8_t fullname_len; OrgUserStatus_t status; }; ''' assert User.__slots__ == () assert ffi.typeof(class_metadata(User, "cdecl")) is ffi.typeof('struct org_user_t) assert ffi.sizeof('struct org_user_t') == class_metadata(User, "csizeof") assert ffi is class_metadata(User, "default_ffi") lib = ffi.dlopen(None) # Allocate using ``ffi.new`` u = User.new(12345, b"Autumn", 6, lib.ACTIVE) assert User.typeof(u) == 'struct org_user_t*' assert ffi.typeof(u.id) is ffi.typeof('uint64_t') assert ffi.typeof(u.fullname) is ffi.typeof('char*') assert ffi.typeof(u.fullname_len) is ffi.typeof('uint8_t') assert len(memoryview(User.getbuffer(u))) == User.sizeof() assert len(memoryview(User.getbuffer(u))) == User.sizeof(u) assert not hasattr(u, 'sizeof') assert u.__internals__["heap"] is None # Allocate using an alternate function # in this case, use the ``.new`` malloc for the # shared page SharedUser: Type[User] = User.with_heap(page) u2 = SharedUser.new() assert u2.__internals__["heap"] is page assert u2.id == 0 assert u2.fullname == ffi.NULL assert u2.fullname_len == 0 assert u2.status == 0 # as far as the cdata is concerned, it points into the ``page``'s heap # User the ``CBase``'s ``.addressof`` call to get a pointer to the entity abs_ptr = ffi.cast('uintptr_t', User.addressof(u2)) assert page.address.begin <= int(abs_ptr) < page.address.end # page contents and buffer match each other assert page.absolute_at[abs_ptr: abs_ptr + User.sizeof()] == User.getbuffer()[0: User.sizeof()] # demo assign # allocate space for a name raw_u2_fullname = page.new('char*', b'Autumn Jolitz') u2.id = 4123 u2.fullname = u2_fullname # assign the pointer u2.fullname_len = 13 u2.status = lib.ACTIVE assert astuple(u2) == (u2.id, raw_u2_fullname, u2.fullname_len, lib.ACTIVE) u2_copy = pickle.loads(pickle.dumps(u2)) assert u2_copy.__internals__["heap"] is page assert astuple(u2) == astuple(u2_copy)
☐ split Locks into RawLock|RawRLock (consumes a memoryview|bytebuffer|ffi.buffer, allocates from 0 to length of lock size)
☐ split Locks into Lock|RLock
☐ reimplement locking in terms of a condition variable
☐ use liblfs for a freelist
☐ make a shared heap process-safe
Limitations
Toy allocator, does not synchronize writes between processes (only parent is expected to have allocated ahead of time).
spawn vs fork
Use “fork” multiprocessing method instead.
The “spawn” multiprocessing method is subject to ASLR and sometimes the kernel locates a child process starting much higher than our process. This has the effect of breaking the requirement for absolute pointers working.
However, it is observed that if a parent process manages to get a high enough memory page, the probability of the child process being able to mmap(2) the same address increases significantly.
import multiprocessing
from contextlib import suppress
from concurrent.futures import ProcessPoolExecutor, wait
from mmap import PAGESIZE
from shmutils import MappedMemory, MapFlags
from shmutils.mmap import munmap, round_to_page_size
from shmutils.shm import shm_open, ffi, shm_unlink
from shmutils.utils import cffiwrapper
# cffiwrapper - use to pickle/unpickle cffi objects between processes
def _set_data_to(value: cffiwrapper, to: int) -> int:
was = value[0]
for i in range(was, to):
value[0] = i
value[0] = to
return was
if __name__ == "__main__":
with suppress(FileNotFoundError):
shm_unlink("test-mmap-spawn")
with shm_open("test-mmap-spawn", "x+") as fd:
shared_size = round_to_page_size(1024 * 1024 * 1024)
fd.truncate(shared_size)
# Allocate a dummy 512 MiB blockrange
unused_space = MappedMemory(None, 512 * 1024 * 1024)
# write to the pages to ensure we're not being fooled
unused_space[len(unused_space) - PAGESIZE : len(unused_space) - PAGESIZE + 4] = b"sink"
# Calculate the last page in the unused space range
start_address: int = unused_space.abs_address_at[len(unused_space) - PAGESIZE]
# detach the unused space guts so we can free all bu the last page
raw_address, size = unused_space.detach()
# free all BUT the last page
munmap(raw_address, size - PAGESIZE)
del unused_space
# Prove our start address is the last page of the mostly freed range
# (our last page is still mapped.)
assert int(ffi.cast("uintptr_t", raw_address)) + size - PAGESIZE == start_address
with MappedMemory(
start_address, shared_size, flags=MapFlags.SHARED | MapFlags.FIXED, fd=fd
) as m:
with ProcessPoolExecutor(1, mp_context=multiprocessing.get_context("spawn")) as exe:
value = m.new("int64_t*", 1923)
assert value[0] == 1923
# The child process will now be able to mess with this counter
future = exe.submit(_set_data_to, cffiwrapper(value, m), 8900)
wait([future])
# And we can see the results both on the value in memory and from the
# return
assert future.done() and not future.exception()
assert (future.result(), value[0]) == (1923, 8900)
Metadata
Release files for shmutils 0.0.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| shmutils-0.0.2.tar.gz | 24.4 kB | Without provenance | Details |
Built distributions (wheels)
Total release size: 638.0 kB
Release files / shmutils-0.0.2.tar.gz
| Download URL | shmutils-0.0.2.tar.gz |
|---|---|
| Size | 24.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ac3b977c3f187ba3677fa8c7833c83beeedf348edb42aeca755986de4b8e2678
|
|
BLAKE2b-256 checksum How to use checksums |
9b83c519cc7a5aa97e91d30bcb1a7f2242d44c4e5e088afb71ec68b55ecaa21f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/4.0.1 CPython/3.9.13
|
Release files / shmutils-0.0.2-cp310-cp310-musllinux_1_1_x86_64.whl
| Download URL | shmutils-0.0.2-cp310-cp310-musllinux_1_1_x86_64.whl |
|---|---|
| Size | 61.2 kB |
| Tags | CPython 3.10 Linux musl 1.1+ x86-64 |
|
SHA-256 checksum How to use checksums |
7dfeeda298efc9d455fc6ce1a720a8c001494a1f382e7c41c8030e9f47f6462a
|
|
BLAKE2b-256 checksum How to use checksums |
cfa16144fea89f7aab5a2008c411a94295b68caf696ad205f30652b1ee20e302
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/4.0.1 CPython/3.9.13
|
Release files / shmutils-0.0.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | shmutils-0.0.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 59.8 kB |
| Tags | CPython 3.10 Linux glibc 2.17+ x86-64 Linux glibc 2.5+ x86-64 |
|
SHA-256 checksum How to use checksums |
9a2402130937158cbe61da1f87bc4c8999b0176f74d3a372d7d1ae25fdd5a74d
|
|
BLAKE2b-256 checksum How to use checksums |
5626fd359f15136a800b085e07ce1e61fe5ad5593b6dad274b35273448e65f5a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/4.0.1 CPython/3.9.13
|
Release files / shmutils-0.0.2-cp310-cp310-macosx_10_9_x86_64.whl
| Download URL | shmutils-0.0.2-cp310-cp310-macosx_10_9_x86_64.whl |
|---|---|
| Size | 32.3 kB |
| Tags | CPython 3.10 macOS 10.9+ x86-64 |
|
SHA-256 checksum How to use checksums |
36510615f267e06bceeb300bf55c896522e63ba4ebb973012413710b68b455ce
|
|
BLAKE2b-256 checksum How to use checksums |
4aa475d1be8dbad3c944e21a10e03077e27259d683a90546ba5b689512c34f9e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/4.0.1 CPython/3.9.13
|
Release files / shmutils-0.0.2-cp39-cp39-musllinux_1_1_x86_64.whl
| Download URL | shmutils-0.0.2-cp39-cp39-musllinux_1_1_x86_64.whl |
|---|---|
| Size | 61.3 kB |
| Tags | CPython 3.9 Linux musl 1.1+ x86-64 |
|
SHA-256 checksum How to use checksums |
c6be8e6a8ca1ababfe16c0f1c436d09648a98599bfe1123e836762d77aea59ef
|
|
BLAKE2b-256 checksum How to use checksums |
c58f43060d06350571b35eb564da031bfa206c8588e370df6a7c0680f794b972
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/4.0.1 CPython/3.9.13
|
Release files / shmutils-0.0.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | shmutils-0.0.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 59.8 kB |
| Tags | CPython 3.9 Linux glibc 2.17+ x86-64 Linux glibc 2.5+ x86-64 |
|
SHA-256 checksum How to use checksums |
24e5e96f10d5625d0ea1704c3b484471ed02b6f7913aa1ffd7b8224f27123113
|
|
BLAKE2b-256 checksum How to use checksums |
8a6065c5ea8615653c95d5a100278b4f1aaf1e8bf7e4e9cc0088d92c43cf89d7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/4.0.1 CPython/3.9.13
|
Release files / shmutils-0.0.2-cp39-cp39-macosx_10_9_x86_64.whl
| Download URL | shmutils-0.0.2-cp39-cp39-macosx_10_9_x86_64.whl |
|---|---|
| Size | 32.3 kB |
| Tags | CPython 3.9 macOS 10.9+ x86-64 |
|
SHA-256 checksum How to use checksums |
73a9b076f36799ce7671d6770fa2af66adee9e43f4bd0510347f9cad42f155ae
|
|
BLAKE2b-256 checksum How to use checksums |
8755572491a8aef88eb292380d7e528fa783bcde1bc894e0df40d3e7102f758e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/4.0.1 CPython/3.9.13
|
Release files / shmutils-0.0.2-cp38-cp38-musllinux_1_1_x86_64.whl
| Download URL | shmutils-0.0.2-cp38-cp38-musllinux_1_1_x86_64.whl |
|---|---|
| Size | 61.7 kB |
| Tags | CPython 3.8 Linux musl 1.1+ x86-64 |
|
SHA-256 checksum How to use checksums |
199a3d3916771aac341a96a3243ae2dc79813ced721fa12ab864762cbd7ae9d0
|
|
BLAKE2b-256 checksum How to use checksums |
827b1093fbb576545f130c03506967a1b1df9e80dff72cab8dc9e655d160729f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/4.0.1 CPython/3.9.13
|
Release files / shmutils-0.0.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | shmutils-0.0.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 59.9 kB |
| Tags | CPython 3.8 Linux glibc 2.17+ x86-64 Linux glibc 2.5+ x86-64 |
|
SHA-256 checksum How to use checksums |
0d5215d68c380c05e1bf758dcfe4e90dc465158a4dae9e1ce3dbf6058bd1547e
|
|
BLAKE2b-256 checksum How to use checksums |
171c94a657db26c7dca23e9852d365805e55d0b6e2b66b6b3b0a6d6f7cb49ba7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/4.0.1 CPython/3.9.13
|
Release files / shmutils-0.0.2-cp38-cp38-macosx_10_9_x86_64.whl
| Download URL | shmutils-0.0.2-cp38-cp38-macosx_10_9_x86_64.whl |
|---|---|
| Size | 32.3 kB |
| Tags | CPython 3.8 macOS 10.9+ x86-64 |
|
SHA-256 checksum How to use checksums |
61a84c3ee80216a8f536fed33269d7ee840571b7eff50767b4afd29f6801f916
|
|
BLAKE2b-256 checksum How to use checksums |
cf4123924229cc59f9a00baa1acba9ea18255df505503c0a6196744b466cc91f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/4.0.1 CPython/3.9.13
|
Release files / shmutils-0.0.2-cp37-cp37m-musllinux_1_1_x86_64.whl
| Download URL | shmutils-0.0.2-cp37-cp37m-musllinux_1_1_x86_64.whl |
|---|---|
| Size | 61.2 kB |
| Tags | CPython 3.7 CPython 3.7 pymalloc Linux musl 1.1+ x86-64 |
|
SHA-256 checksum How to use checksums |
4f8345eb917f3113b5aac86b82b4602376c16d82968cb9e6b1bd5e786fbbcbd5
|
|
BLAKE2b-256 checksum How to use checksums |
6e610d521583149da4e6d4b6f212442722cd35d8a476819faf349ad92c22be1d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/4.0.1 CPython/3.9.13
|
Release files / shmutils-0.0.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | shmutils-0.0.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 59.7 kB |
| Tags | CPython 3.7 CPython 3.7 pymalloc Linux glibc 2.17+ x86-64 Linux glibc 2.5+ x86-64 |
|
SHA-256 checksum How to use checksums |
4bb5c93a3258209d86e8c477e7e1dc8e6bd9c9d1321fd05ef3ef28320ac8b173
|
|
BLAKE2b-256 checksum How to use checksums |
93e436ffe833503a962056d65877f4c923d3b342434b60a253433bb1f429c883
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/4.0.1 CPython/3.9.13
|
Release files / shmutils-0.0.2-cp37-cp37m-macosx_10_9_x86_64.whl
| Download URL | shmutils-0.0.2-cp37-cp37m-macosx_10_9_x86_64.whl |
|---|---|
| Size | 32.2 kB |
| Tags | CPython 3.7 CPython 3.7 pymalloc macOS 10.9+ x86-64 |
|
SHA-256 checksum How to use checksums |
54cd88acc60dbf2812d7ec8e0d2c319578d5616673eb6d9f2a543038fabe50d6
|
|
BLAKE2b-256 checksum How to use checksums |
3b96af5dcdbdf0c41184a572e0ffc068d0dd8737b276ba6f3b9b6d8a3c8b7ae4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/4.0.1 CPython/3.9.13
|
Security for shmutils 0.0.2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.