Windows ring buffer without copying

2020-07-11 04:27发布

On Ring Buffer's Wikipedia entry, there's example code showing a hack for UNIX systems whereby the adjacent virtual memory to a piece of memory is mapped to the same phbysical memory, thus implementing a ring buffer without the need for any memcpy, etc. I was wondering if there's a way to so something similar in Windows?

Thanks, Fraser

2条回答
乱世女痞
2楼-- · 2020-07-11 04:40

I didn't really follow all the details of the example in wikipedia. With that in mind, you map memory in Windows using CreateFileMapping and MapViewOfFile, however MapViewOfFile does not allow you to specify a base address for the mapping. MapViewOfFileEx can be used to specify a base address so maybe you could use a similar technique.

I don't have any way of telling if this would actually work:

// determine valid buffer size
SYSTEM_INFO info;
GetSystemInfo(&info);

// note that the base address must be a multiple of the allocation granularity
DWORD bufferSize=info.dwAllocationGranularity;

HANDLE hMapFile = CreateFileMapping(
             INVALID_HANDLE_VALUE,
             NULL,
             PAGE_READWRITE,
             0,
             bufferSize*2,
             L"Mapping");

BYTE *pBuf = (BYTE*)MapViewOfFile(hMapFile,
                    FILE_MAP_ALL_ACCESS,
                    0,                   
                    0,                   
                    bufferSize);
MapViewOfFileEx(hMapFile,
                    FILE_MAP_ALL_ACCESS,
                    0,                   
                    0,                   
                    bufferSize,
                    pBuf+bufferSize);
查看更多
三岁会撩人
3楼-- · 2020-07-11 04:47

Oh hey, this is the topic which worried me a lot lately. I needed posix-optimised ring buffer on Windows, mostly because of its random-access interface, but never had any idea on how to implement it. Now, the code proposed by @1800 INFORMATION works sometimes, sometimes it doesn't, but the idea is great anyway.

The thing is, MapViewOfFileEx sometimes fails with ERROR_INVALID_ADDRESS meaning that it cannot map the view to pBuf+bufferSize. This is because the MapViewOfFile called before selects a free address space of bufferSize length (starting from pBuf), but it doesn't garantee this address space to be bufferSize*2 long. And why would we need bufferSize*2 virtual memory? Because our ring buffer needs to wrap. This is what the second mapping view is for. When the read or write pointer leaves the first view, it enters the second view (because they are contigous in memory), but actually it starts over at the same mapping.

UINT_PTR addr;
HANDLE hMapFile;
LPVOID address, address2;

hMapFile = CreateFileMapping (    // create a mapping backed by a pagefile
    INVALID_HANDLE_VALUE,
    NULL,
    PAGE_EXECUTE_READWRITE,
    0,
    bufferSize*2,
    "Local\\mapping" );
if(hMapFile == NULL) 
    FAIL(CreateFileMapping);

address = MapViewOfFile (    // find a free bufferSize*2 address space
    hMapFile,
    FILE_MAP_ALL_ACCESS,
    0,                   
    0,                   
    bufferSize*2 );
if(address==NULL) 
    FAIL(MapViewOfFile);
UnmapViewOfFile(address);
// found it. hopefully it'll remain free while we map to it

addr = ((UINT_PTR)address);
address = MapViewOfFileEx (
    hMapFile,
    FILE_MAP_ALL_ACCESS,
    0,                   
    0,                   
    bufferSize, 
    (LPVOID)addr );

addr = ((UINT_PTR)address) + bufferSize;        
address2 = MapViewOfFileEx (
    hMapFile,
    FILE_MAP_ALL_ACCESS,
    0,                   
    0,                   
    bufferSize,
    (LPVOID)addr);  

if(address2==NULL)      
    FAIL(MapViewOfFileEx);

// when you're done with your ring buffer, call UnmapViewOfFile for 
// address and address2 and CloseHandle(hMapFile)
查看更多
登录 后发表回答