共享内存 - windows平台和linux平台的不同处理

80 篇文章 0 订阅
39 篇文章 1 订阅
本文介绍了如何在Linux和Windows平台上使用共享内存进行模块分割和复杂性隔离,包括Python的`posix_ipc`和`multiprocessing`库的示例。同时讨论了跨平台移植中的注意事项,以及运行时可能出现的UserWarning和BufferError异常及其处理方法。
摘要由CSDN通过智能技术生成

1.各个平台下的共享内存的使用方法

共享内存主要用来进行模块分割,复杂性隔离的用途。python这类跨平台平台其实是无法完全消弭各个平台间的个体差异,一个简单的例子:

import posix_ipc
import mmap

# 创建或打开共享内存
shm = posix_ipc.SharedMemory('/example', posix_ipc.O_CREAT, mode=0o666, size=100)

# 映射共享内存到当前进程
shmmap = mmap.mmap(shm.fd, shm.size)

# 在共享内存中写入数据
shmmap.write(b'Hello, shared memory!')

# 在另一个进程中读取共享内存中的数据
shm2 = posix_ipc.SharedMemory('/example')
shmmap2 = mmap.mmap(shm2.fd, shm2.size)

content = shmmap2.read()
content = content.rstrip(b'\x00');
print("%s" %content)  # 输出 b'Hello, shared memory!'

# 释放共享内存
shmmap2.close()
shm2.close_fd()
shmmap.close()
shm.close_fd()
shm.unlink()

上面是linux下的共享内存使用一例,有多种途径,这是其中的一种。但是windows平台下,它是这样的:

from multiprocessing.shared_memory import SharedMemory
from typing import Iterator
from multiprocessing.managers import SharedMemoryManager
from multiprocessing import Process, Queue
import numpy as np
import uuid
import traceback
import time
from contextlib import contextmanager
import uuid


#from process_call_func_test import child_process

# 创建子进程,共享内存会被子进程继承
def child_process(name):
    print("child:",name)
    try:
        # 在子进程中访问共享内存数据
        shm = gp_shared_memory_open(name)
        shared_data = gp_shared_memory_get_uint8_of(shm)
        print(shared_data)
        shared_data = None
        shm.close()
    except Exception as e:
        traceback.print_exc()
        print(f"Exception in child process: {e}")
    print("child quit!")
    return 1976

def gp_shared_memory_create(name, size):
    # 自行指定共享内存的名称
    shm_name = name
    shm_size = size
    shm = SharedMemory(name=shm_name, create=True, size=shm_size)
    return shm

def gp_shared_memory_open(name):
    # 在子进程中访问共享内存数据
    shm_name = name
    shm = SharedMemory(name=shm_name, create=False)
    return shm

def gp_shared_memory_get_uint8_of(shm) -> Iterator[SharedMemory]:
    # 在主进程中写入数据到共享内存
    ar = np.frombuffer(shm.buf, dtype=np.uint8);
    return ar

def gp_shared_memory_close(shm):
    shm.close()

def gp_get_a_randome_name():
    # 生成一个新的 GUID
    new_guid = uuid.uuid4()
    name = str(new_guid)
    return name

# 创建子进程,共享内存会被子进程继承
def test_gp_shared_memory():
    if __name__ == "__main__":
        name = gp_get_a_randome_name()
        shm = gp_shared_memory_create(name, 16)
        buf_in_u8 = gp_shared_memory_get_uint8_of(shm)
        buf_in_u8[1] = 19
        buf_in_u8[2] = 76
        buf_in_u8 = None
        # 在子进程中访问共享内存数据
        # 启动子进程
        p = Process(target=child_process, args=(shm.name,))
        p.start()
        p.join(3.5)
        if p.is_alive():
            # 子进程尚未结束,进行相应的处理
            print("Child Process timeout...")
            # 可以尝试终止子进程
            p.terminate()
            # 等待子进程终止
            p.join()
        else:
            time.sleep(1.5)
            print("child proc done!")
        gp_shared_memory_close(shm)

if __name__ == "__main__":
    test_gp_shared_memory()

Windows下面的multiprocessing库根本没有shardmemory这个子项, 它被放置在别的地方。

2.跨平台移植的注意事项

Python乃至任何一个组织,都没有足够的能力把所有的差异,或大或小的沟渠填平,你只能适应。分歧是必然的,同步,和谐有,更可能没有。一些思维僵化的人无法在舒适圈外有效拓展认知。今天一清早被朋友数落了一句:你是错把风口当本事。。。

这个地方我卡了大概三个小时,老顽固们大部分工作时间,都耗在卡顿上。不是不行,是脑袋跟不上时代——其实就是不行。调试,代码实验,是小白型程序员的看家本领。

3. 运行时异常及处理

1.“UserWarning: resource_tracker: There appear to be 1 leaked shared_memory objects to clean up at shutdown

这个问题是因为Python在应用程序退出时做了一些额外的防护,它会检查程序退出时,是否有sharedMemory没有释放——甚至会帮你 unlink()  :

https://github.com/python/cpython/issues/89372

这个错误,真不该犯,不知道它波及面有多少。处理方法:

# 使用monkeypatch给python代码打补丁:
# 解决python程序在退出时自动销毁关联的sharedmemory的bug.
# 你需要把patch_ban_shm_tracing()放在你的python应用程序中。
# 相关原始信息:
#     https://github.com/python/cpython/issues/82300

def fix_register(name, rtype):
    if rtype == "shared_memory":
        return
    return resource_tracker._resource_tracker.register(self, name, rtype)

def fix_unregister(name, rtype):
    if rtype == "shared_memory":
        return
    return resource_tracker._resource_tracker.unregister(self, name, rtype)

def patch_ban_shm_tracing():
    resource_tracker.register = fix_register
    resource_tracker.unregister = fix_unregister
    if "shared_memory" in resource_tracker._CLEANUP_FUNCS:
        del resource_tracker._CLEANUP_FUNCS["shared_memory"]

如果你不需要把shared memory unlink。你需要在python程序执行完毕时,手工添加这条指令。

 2."BufferError: cannot close exported pointers exist"

这个异常在发生在shm.close()时。如果类似下面

ar = np.frombuffer(shm.buf, dtype=np.uint8);

这类从shm.buf层层传递出来的对象在调用shm.close()前被显式回收,shm.close()会侦测到这些空指针类似的对象并报错。
所以,解决方案是:从shm辗转引申出的缓冲区都要手工显式回收:

del ar
ar = None

附录A 完整的linux下的shared memory封装

附带有测试例程(Usage)

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import traceback
import uuid
import posix_ipc
import mmap
import time
from multiprocessing import Process


def _gp_shared_memory_getmmap_of(shm):
    # 在主进程中写入数据到共享内存
    mmap_ret = mmap.mmap(shm.fd, shm.size);
    return mmap_ret

def _gp_shared_memory_create(r_name, r_size):
    # 自行指定共享内存的名称
    shm_name = r_name
    shm = posix_ipc.SharedMemory(shm_name, posix_ipc.O_CREAT, mode=0o666, size=r_size)
    mmap = _gp_shared_memory_getmmap_of(shm)
    return (shm, mmap)


def _gp_shared_memory_open(name):
    # 自行指定共享内存的名称
    shm_name = name
    shm = posix_ipc.SharedMemory(shm_name)
    mmap = _gp_shared_memory_getmmap_of(shm)
    return (shm, mmap)

def gp_get_a_random_name():
    # 生成一个新的 GUID
    new_guid = uuid.uuid4()
    name = str(new_guid)
    return name

class gp_shared_memory_owner:
    def __init__(self, r_name, r_size):
        if(r_name is None):
            r_name = gp_get_a_random_name()
        self._name = r_name
        (self._shm, self._mmap) = _gp_shared_memory_create(r_name, r_size)
        self._size = self._shm.size

    def getmmap(self):
        return self._mmap

    def getname(self):
        return self._name

    def getsize(self):
        return self._size

    def release(self):
        self._mmap.close()
        self._shm.close_fd()
        self._shm.unlink()
        self._name = None
        self._size = 0
        self._mmap = None
        self._shm = None
    

class gp_shared_memory_client:
    def __init__(self, r_name):
        self._name = r_name
        (self._shm, self._mmap) = _gp_shared_memory_open(r_name)
        self._size = self._shm.size

    def getmmap(self):
        return self._mmap;
    def getname(self):
        return self._name;
    def getsize(self):
        return self._size;
    def release(self):
        self._mmap.close()
        self._shm.close_fd()
        self._name = None
        self._size = 0
        self._mmap = None
        self._shm = None

# 创建子进程,共享内存会被子进程继承
def test_child_process(name):
    print("child:",name)
    try:
        # 在子进程中访问共享内存数据
        shmClient = gp_shared_memory_client(name)
        content = shmClient.getmmap().read()
        content = content.rstrip(b'\x00');
        print("child process got msg: ", content)
        shmClient.release()
    except Exception as e:
        traceback.print_exc()
        print(f"Exception in child process: {e}")
    print("child quit!")
    return 1976

# 创建子进程,共享内存会被子进程继承
def test_gp_shared_memory():
    if __name__ == "__main__":
        shmOwner1 = gp_shared_memory_owner(None,16)
        shmOwner1.getmmap().write(b'0x190x76')
        # 在子进程中访问共享内存数据
        # 启动子进程
        p = Process(target=test_child_process, args=(shmOwner1.getname(),))
        p.start()
        p.join(3.5)
        if p.is_alive():
            # 子进程尚未结束,进行相应的处理
            print("Child Process timeout...")
            # 可以尝试终止子进程
            p.terminate()
            # 等待子进程终止
            p.join()
        else:
            time.sleep(1.5)
            print("child proc done!")
        shmOwner1.release()

if __name__ == "__main__":
    test_gp_shared_memory()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

子正

thanks, bro...

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值