【问题标题】:Segmentation Fault using Python Shared Memory使用 Python 共享内存的分段错误
【发布时间】:2020-09-02 20:40:21
【问题描述】:

函数store_in_shm 将一个numpy 数组写入共享内存,而第二个函数read_from_shm 使用同一共享内存空间中的数据创建一个numpy 数组并返回该numpy 数组。

但是,在 Python 3.8 中运行代码会出现以下分段错误:

zsh: 分段错误 python foo.py

为什么从函数read_from_shm内部访问numpy数组没有问题,但是在函数外部再次访问numpy数组时出现分段错误?

输出:

From read_from_shm(): [0 1 2 3 4 5 6 7 8 9]
zsh: segmentation fault  python foo.py
% /Users/athena/opt/anaconda3/envs/test/lib/python3.8/multiprocessing/resource_tracker.py:203: UserWarning: resource_tracker: There appear to be 1 leaked shared_memory objects to clean up at shutdown
  warnings.warn('resource_tracker: There appear to be %d '

foo.py

import numpy as np
from multiprocessing import shared_memory

def store_in_shm(data):
    shm = shared_memory.SharedMemory(name='foo', create=True, size=data.nbytes)
    shmData = np.ndarray(data.shape, dtype=data.dtype, buffer=shm.buf)
    shmData[:] = data[:]
    shm.close()
    return shm

def read_from_shm(shape, dtype):
    shm = shared_memory.SharedMemory(name='foo', create=False)
    shmData = np.ndarray(shape, dtype, buffer=shm.buf)
    print('From read_from_shm():', shmData)
    return shmData

if __name__ == '__main__':
    data = np.arange(10)
    shm = store_in_shm(data)
    shmData = read_from_shm(data.shape, data.dtype)
    print('From __main__:', shmData)    # no seg fault if we comment this line
    shm.unlink()

【问题讨论】:

  • 我通常在使用 python 的多处理管理器时遇到问题,只使用multiprocessing.sharedctypes.RawArraymultiprocessing.Lock 进行手动同步。通过您的示例,我实际上在 Mac 与 Windows 中得到了不同的答案,其中 Windows 在同一进程中成为 close()d 后似乎无法重新打开文件。您收到的错误表明它希望您也可以在 read_from_shm 中使用 close shm,因为它表示您不是 close() 正在做某事。
  • 显然在 Windows 中 unlink 实际上什么都不做,并且一旦所有当前打开的副本都关闭,文件就会被销毁,因此内存在 store_in_shmread_from_shm 之间被销毁,从而导致文件未找到错误。
  • @Aaron 如果我们在read_from_shm 内执行shm.close(),我认为numpy 数组shmData 在访问shm 中的缓冲区时遇到问题,因为shm 现在已关闭。
  • 是的,我仍在寻找它应该如何工作。如果我真的弄清楚了,我会发布一个实际的答案......
  • 我最终可能不得不撤回我关于更喜欢 sharedctypes 的声明。在深入研究代码之后,似乎shared_memory 可能更简单、更轻量级(尽管它们在引擎盖下确实以类似的方式工作)。显然还有一些错误需要解决......

标签: python macos numpy shared-memory python-3.8


【解决方案1】:

基本上,问题似乎是当函数返回时对shm 进行垃圾收集时,底层mmap'ed 文件(由read_from_shm 内的shm 拥有)正在关闭。然后shmData 引用它,这是你得到segfault 的地方(用于引用封闭的mmap)这似乎是known bug,但可以通过保留对shm 的引用来解决。

此外,所有SharedMemory 实例都希望成为close()'d,其中一个在不再需要时被unlink()'ed。如果您不自己调用shm.close(),它将在GC 上调用,如前所述,并且在Windows 上,如果它是当前唯一“打开”的共享内存文件将被删除。当您在store_in_shm 中调用shm.close() 时,您会引入操作系统依赖项,因为在Windows 上数据将被删除,而在MacOS 和Linux 上,它将一直保留到调用unlink

最后,虽然这没有出现在您的代码中,但 another problem 当前存在,从独立进程(而不是子进程)访问数据同样会过早地删除底层 mmap。 SharedMemory 是一个非常新的库,希望所有问题都能很快解决。

您可以重新编写给定的示例以保留对“第二个”shm 的引用,并且只使用其中一个到 unlink

import numpy as np
from multiprocessing import shared_memory

def store_in_shm(data):
    shm = shared_memory.SharedMemory(name='foo', create=True, size=data.nbytes)
    shmData = np.ndarray(data.shape, dtype=data.dtype, buffer=shm.buf)
    shmData[:] = data[:]
    #there must always be at least one `SharedMemory` object open for it to not
    #  be destroyed on Windows, so we won't `shm.close()` inside the function,
    #  but rather after we're done with everything.
    return shm

def read_from_shm(shape, dtype):
    shm = shared_memory.SharedMemory(name='foo', create=False)
    shmData = np.ndarray(shape, dtype, buffer=shm.buf)
    print('From read_from_shm():', shmData)
    return shm, shmData #we need to keep a reference of shm both so we don't
                        #  segfault on shmData and so we can `close()` it.

if __name__ == '__main__':
    data = np.arange(10)
    shm1 = store_in_shm(data)
    #This is where the *Windows* previously reclaimed the memory resulting in a 
    #  FileNotFoundError because the tempory mmap'ed file had been released.
    shm2, shmData = read_from_shm(data.shape, data.dtype)
    print('From __main__:', shmData)
    shm1.close() 
    shm2.close()
    #on windows "unlink" happens automatically here whether you call `unlink()` or not.
    shm2.unlink() #either shm1 or shm2

【讨论】:

  • 从逻辑上讲,您仍然必须拥有对 SharedMemory 对象的引用才能关闭它,并且您可能不会假设在关闭后您仍然可以访问它支持的数据(numpy 数组)。似乎this 可能是错过取消引用并引发段错误的根本原因。
  • "保留对 shm 的引用" 有没有办法在某处存储引用以自动解决此错误?我想创建大量 shared_memory 支持的 numpy 数组。我想我可以保留一个巨大的指针列表并在创建新数组时附加对它的引用?有没有更 Pythonic 的方式来做到这一点?
  • @ike 可能会创建一个 ndarray 的子类,它处理从缓冲区创建数组然后为您挂起引用?然后可能应该定义 __del__ 来调用 unlink 以便在 *nix 系统上不再需要文件时将其删除
  • 问题是,我正在不同线程中创建由相同缓冲区支持的不同 ndarray。我需要将这些引用存储在某处,但我再次遇到相同的共享问题 - 如果我只将它们保留在分配它们的线程中,我似乎会遇到段错误。
  • @ike obligatory:“要非常小心地混合线程和进程:fork 与多线程完全不匹配”这与 OP 的问题基本相同......你必须总是有一个引用某处或支持它的临时文件将被操作系统删除(当所有句柄关闭时删除文件)。老实说,在这种情况下,我只是在父进程的模块级别创建一个列表,并将所有 shm 对象填充到其中,然后在末尾执行类似获取列表长度(任何真正引用它的内容)的操作这个过程,所以它永远不会被垃圾收集。
猜你喜欢
  • 1970-01-01
  • 2018-03-12
  • 1970-01-01
  • 2018-06-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-13
  • 1970-01-01
相关资源
最近更新 更多