mmap:无法在不知道大小的情况下附加到现有区域(Windows)

我试图附加到由另一个应用程序创build的现有的共享内存区域,而不是用Python编写(这是它的插件模块如何相互通信)。 在Windows上,它使用命名的内核对象而不是文件系统中的文件; Python的mmap模块通过tagname参数来支持这一点。 问题是,我无法预先知道共享区域的大小 – 这是另一个应用程序的configuration参数,根据预期的数据量进行调整。 对于基于文件的共享区域,传递零大小使用文件的现有大小,但这显然不适用于标记的区域。 以下是我正在尝试的简化版本:

 import mmap, random TAGNAME = 'SHM_1001' # This is a simulation of what the other application does. # The size isn't actually random, I simply don't know in advance what it is. m1 = mmap.mmap(-1, random.randint(1e3, 1e6), TAGNAME) # This is what I'm trying to do in my application, to attach to the same region. m2 = mmap.mmap(-1, 0, TAGNAME) # WindowsError: [Error 87] The parameter is incorrect 

如果我指定一个小的非零大小,那么我可以成功地连接到这个区域 – 但是我当然可以在这个区域的开头只能访问那么多的字节。 如果我指定的尺寸大于该区域的实际尺寸(可能等于它所能达到的最大尺寸),则会出现访问错误。 Python 2.7和3.4都存在这个问题。

传递零大小的方法肯定能够在系统调用级别工作 – 这就是每个现有的C / C ++插件对于这个应用程序的工作原理 – 所以这个问题显然在Python的mmap()调用封装中。 任何想法,我怎么能得到这个工作?

在调用系统服务NtCreateSection之前, CreateFileMapping的参数验证将会出错,如果调用该函数将会找到现有的部分。 当hFileINVALID_HANDLE_VALUE (-1)时,使用0大小是无效的,因为CreateFileMapping假设(在这种情况下错误地)该部分需要从分页文件中分配。 我假设C插件是调用OpenFileMapping (即NtOpenSection )。

您可以使用ctypes或PyWin32或C扩展模块。 调用OpenFileMappingW ,调用MapViewOfFile ,然后调用VirtualQuery来获取映射的区域大小,向上舍入到页面边界。

这里是一个使用ctypes的例子。

 from ctypes import * from ctypes.wintypes import * kernel32 = WinDLL('kernel32', use_last_error=True) FILE_MAP_COPY = 0x0001 FILE_MAP_WRITE = 0x0002 FILE_MAP_READ = 0x0004 FILE_MAP_ALL_ACCESS = 0x001f FILE_MAP_EXECUTE = 0x0020 PVOID = LPVOID SIZE_T = c_size_t class MEMORY_BASIC_INFORMATION(Structure): _fields_ = (('BaseAddress', PVOID), ('AllocationBase', PVOID), ('AllocationProtect', DWORD), ('RegionSize', SIZE_T), ('State', DWORD), ('Protect', DWORD), ('Type', DWORD)) PMEMORY_BASIC_INFORMATION = POINTER(MEMORY_BASIC_INFORMATION) def errcheck_bool(result, func, args): if not result: raise WinError(get_last_error()) return args kernel32.VirtualQuery.errcheck = errcheck_bool kernel32.VirtualQuery.restype = SIZE_T kernel32.VirtualQuery.argtypes = ( LPCVOID, # _In_opt_ lpAddress PMEMORY_BASIC_INFORMATION, # _Out_ lpBuffer SIZE_T) # _In_ dwLength kernel32.OpenFileMappingW.errcheck = errcheck_bool kernel32.OpenFileMappingW.restype = HANDLE kernel32.OpenFileMappingW.argtypes = ( DWORD, # _In_ dwDesiredAccess BOOL, # _In_ bInheritHandle LPCWSTR) # _In_ lpName kernel32.MapViewOfFile.errcheck = errcheck_bool kernel32.MapViewOfFile.restype = LPVOID kernel32.MapViewOfFile.argtypes = ( HANDLE, # _In_ hFileMappingObject DWORD, # _In_ dwDesiredAccess DWORD, # _In_ dwFileOffsetHigh DWORD, # _In_ dwFileOffsetLow SIZE_T) # _In_ dwNumberOfBytesToMap kernel32.CloseHandle.errcheck = errcheck_bool kernel32.CloseHandle.argtypes = (HANDLE,) if __name__ == '__main__': import mmap NPAGES = 9 PAGE_SIZE = 4096 TAGNAME = 'SHM_1001' mm1 = mmap.mmap(-1, PAGE_SIZE * NPAGES, TAGNAME) hMap = kernel32.OpenFileMappingW(FILE_MAP_ALL_ACCESS, False, TAGNAME) pBuf = kernel32.MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0) kernel32.CloseHandle(hMap) mbi = MEMORY_BASIC_INFORMATION() kernel32.VirtualQuery(pBuf, byref(mbi), PAGE_SIZE) assert divmod(mbi.RegionSize, PAGE_SIZE) == (NPAGES, 0) mm2 = (c_char * mbi.RegionSize).from_address(pBuf) # write using the mmap object mm1.seek(100) mm1.write(b'Windows') # read using the ctypes array assert mm2[100:107] == b'Windows' 

它应该像这样工作:

如果长度大于文件的当前大小,则将文件扩展为包含长度字节。 如果长度为0,则映射的最大长度是文件的当前大小,但是如果文件为空,则Windows引发异常(无法在Windows上创建空映射)。

但目前看来,这是一个已知的错误: http : //www.gossamer-threads.com/lists/python/bugs/571941? search_string = 1733986;# 571941