Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/330.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/tfs/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python ctypes从带有上下文管理器的缓冲区映射到内存映射文件(mmap)_Python_Python 3.x_Ctypes_Mmap_Contextmanager - Fatal编程技术网

Python ctypes从带有上下文管理器的缓冲区映射到内存映射文件(mmap)

Python ctypes从带有上下文管理器的缓冲区映射到内存映射文件(mmap),python,python-3.x,ctypes,mmap,contextmanager,Python,Python 3.x,Ctypes,Mmap,Contextmanager,我正在使用ctypes.from_buffer()为某些任务将ctypes结构映射到内存映射文件。通常,这些文件包含结构化头和二进制数据的串联。ctypes结构允许稳定的二进制表示和轻松的pythonic字段访问—这是一个真正的获胜团队 这些内存映射文件随时间动态增长。除了只接受mmap.PAGESIZE粒度增长的复杂性外,如果在调整大小尝试期间将一些(隐藏的)引用保留在映射区域中,mmap会产生过敏反应 这就是上下文管理器发挥作用的地方 # -*- coding: utf8 -* impor

我正在使用
ctypes.from_buffer()
为某些任务将ctypes结构映射到内存映射文件。通常,这些文件包含结构化头和二进制数据的串联。ctypes结构允许稳定的二进制表示和轻松的pythonic字段访问—这是一个真正的获胜团队

这些内存映射文件随时间动态增长。除了只接受
mmap.PAGESIZE
粒度增长的复杂性外,如果在调整大小尝试期间将一些(隐藏的)引用保留在映射区域中,mmap会产生过敏反应

这就是上下文管理器发挥作用的地方

# -*- coding: utf8 -*

import io
import mmap
import ctypes
import logging

log = logging.getLogger(__file__)

def align(size, alignment):
    """return size aligned to alignment"""
    excess = size % alignment
    if excess:
        size = size - excess + alignment
    return size

class CtsMap:
    def __init__(self, ctcls, mm, offset = 0):
        self.ctcls = ctcls
        self.mm = mm
        self.offset = offset

    def __enter__(self):
        mm = self.mm
        offset = self.offset
        ctsize = ctypes.sizeof(self.ctcls)
        if offset + ctsize > mm.size():
            newsize = align(offset + ctsize, mmap.PAGESIZE)
            mm.resize(newsize)
        self.ctinst = self.ctcls.from_buffer(mm, offset)
        log.debug('add mapping: %s', ctypes.addressof(self.ctinst))
        return self.ctinst

    def __exit__(self, exc_type, exc_value, exc_traceback):
        # free all references into mmap
        log.debug('remove mapping: %s', ctypes.addressof(self.ctinst))
        del self.ctinst
        self.ctinst = None

class MapFile:
    def __init__(self, filename):
        self._offset = 0
        try:
            mapsize = mmap.PAGESIZE
            self._fd = open(filename, 'x+b')
            self._fd.write(b'\0' * mapsize)
            self._created = True
        except FileExistsError:
            self._fd = open(filename, 'r+b')
            self._fd.seek(0, io.SEEK_END)
            mapsize = self._fd.tell()
        self._fd.seek(0)
        self._mm = mmap.mmap(self._fd.fileno(), mapsize)

    def add_data(self, data):
        datasize = len(data)
        log.debug('add_data: header')
        hdtype = ctypes.c_char * 4
        with CtsMap(hdtype, self._mm, self._offset) as hd:
            hd.raw = b'HEAD'
            self._offset += 4
        #del hd
        log.debug('add_data: %s', datasize)
        blktype = ctypes.c_char * datasize
        with CtsMap(blktype, self._mm, self._offset) as blk:
            blk.raw = data
            self._offset += datasize
        #del blk
        return 4 + datasize

    def size(self):
        return self._mm.size()

    def close(self):
        self._mm.close()
        self._fd.close()

if __name__ == '__main__':
    import sys

    logconfig = dict(
        level = logging.DEBUG,
        format = '%(levelname)5s: %(message)s',
    )
    logging.basicConfig(**logconfig)

    mapfile = sys.argv[1:2] or 'mapfile'
    datafile = sys.argv[2:3] or __file__

    data = open(datafile, 'rb').read()

    maxsize = 10 * mmap.PAGESIZE

    mf = MapFile(mapfile)
    while mf.size() < maxsize:
        mf.add_data(data)
    mf.close()
要使代码正常工作,必须删除MapFile.add_data中两个del语句前面的注释

显然,它们是必需的,因为
with
语句中分配的变量仍然存在于本地名称空间中,这足以将引用保留到
mmap.resize()
偶然发现的mmap区域中

我怎样才能摆脱这些del语句,因为它们是真正的PITA,而上下文管理器不是为了避免这种扭曲而发明的吗

有没有办法更有效地删除这些映射?例如,在
CtsMap.\uu退出
中以编程方式从缓冲区()恢复
ctypes.的操作


可以找到一个相关的问题,它进一步说明了ctypes.from_buffer与mmaped files方法的结合。

Hmm,代码在Windows上运行时没有错误,并创建了
mapfile
。不能复制。不过,我怀疑
\uuuu exit\uuuu
中的
self.mm=None
会释放出
del
正在释放的东西。嗯,我在Linux上(相当流行的内核FWIW)。不幸的是,这两个操作系统之间的mmap实现存在显著差异。我不知道的是
with
变量的生命周期语义。当带
块结束时,var超出范围,我认为,这足以触发析构函数,因此理论上,
\uuuuuuuu退出
中的代码就足够了。但实际上,在Linux下的Python 3.4.5中,情况并非如此。感谢您在Windows下对此进行测试。
with
变量没有超出范围;只有
\uuuuu退出\uuuuu
被调用。由于
del
为您修复了一些问题,因此将实例变量设置为None将释放这些对象。
DEBUG: add_data: header
DEBUG: add mapping: 139989829832704
DEBUG: remove mapping: 139989829832704
DEBUG: add_data: 3275
DEBUG: add mapping: 139989829832708
DEBUG: remove mapping: 139989829832708
DEBUG: add_data: header
DEBUG: add mapping: 139989829835983
DEBUG: remove mapping: 139989829835983
DEBUG: add_data: 3275
Traceback (most recent call last):
  File "ctxmmapctypes.py", line 110, in <module>
    mf.add_data(data)
  File "ctxmmapctypes.py", line 78, in add_data
    with CtsMap(blktype, self._mm, self._offset) as blk:
  File "ctxmmapctypes.py", line 39, in __enter__
    mm.resize(newsize)
BufferError: mmap can't resize with extant buffers exported.