我正在尝试附加到另一个应用程序创建的现有共享内存区域,而不是用Python编写(这是它的插件模块如何相互通信)。在Windows上,它使用命名的内核对象而不是文件系统中的文件; Python的mmap模块通过tagname
参数支持此功能。问题是我无法事先知道共享区域的大小是什么 - 这是另一个应用程序的配置参数,它是根据预期的数据量进行调整的。对于基于文件的共享区域,为大小传递零使用文件的现有大小,但这显然不适用于标记区域。这是我正在尝试的简化版本:
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()调用包装器中。关于如何让它发挥作用的任何想法?
答案 0 :(得分:1)
CreateFileMapping
中的参数验证在调用系统服务NtCreateSection
之前发生错误,如果调用它将找到现有部分。当hFile
为INVALID_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'
答案 1 :(得分:0)
它应该像这样工作:
如果length大于文件的当前大小,则文件为 扩展为包含长度字节。如果长度为0,则为最大长度 映射的大小是文件的当前大小,除非文件是 空Windows引发异常(您无法创建空映射 在Windows上。)
但目前看来,这是一个已知的错误:http://www.gossamer-threads.com/lists/python/bugs/571941?search_string=1733986;#571941