【问题标题】:Windows ring buffer without copying无需复制的 Windows 环形缓冲区
【发布时间】:2009-06-19 08:21:05
【问题描述】:

Ring Buffer's Wikipedia entry 上,example code 显示了针对UNIX 系统的黑客攻击,其中与一块内存相邻的虚拟内存是mapped 与同一物理内存,因此无需任何memcpy 等。我想知道Windows 中是否有类似的方法?

谢谢,弗雷泽

【问题讨论】:

  • 所讨论的示例并没有消除对 memcpy 的需要,它消除了在操作将超过缓冲区分配结束时在两个片段中执行 DMA 操作的需要。它只能在虚拟地址空间中工作,因为它依赖于将页面大小的缓冲区映射到虚拟内存中的两个位置。
  • 呃……是的,那个。在我的例子中,我将指针传递给另一个(库)函数进行写入,以便该函数可以执行自己的 memcpy。

标签: c windows memory circular-buffer


【解决方案1】:

我并没有真正遵循维基百科中示例的所有细节。考虑到这一点,您可以使用CreateFileMappingMapViewOfFile 在 Windows 中映射内存,但是 MapViewOfFile 不允许您为映射指定基地址。 MapViewOfFileEx 可用于指定基地址,因此也许您可以使用类似的技术。

我不知道这是否真的有效:

// 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);

【讨论】:

  • 对不起我最初的反应。我真的没想到会奏效
  • ;-P 实际上,我认为 CreateFileMapping 只需要分配 bufferSize,而不是 bufferSize*2...反正我没有遇到访问冲突。
【解决方案2】:

哦,嘿,这是我最近很担心的话题。我需要在 Windows 上进行 posix 优化的环形缓冲区,主要是因为它的随机访问接口,但从来不知道如何实现它。现在,@1800 INFORMATION 提出的代码有时有效,有时无效,但无论如何这个想法很棒。

问题是,MapViewOfFileEx 有时会失败并显示 ERROR_INVALID_ADDRESS,这意味着它无法将视图映射到 pBuf+bufferSize。这是因为之前调用的MapViewOfFile 选择了一个bufferSize 长度的空闲地址空间(从pBuf 开始),但它不保证这个地址空间是bufferSize*2 长度。为什么我们需要bufferSize*2 虚拟内存?因为我们的环形缓冲区需要换行。这就是第二个映射视图的用途。当读或写指针离开第一个视图时,它会进入第二个视图(因为它们在内存中是连续的),但实际上它是从同一个映射开始的。

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)

【讨论】:

  • "它不保证这个地址空间是 bufferSize*2 长" -- 这可以通过使用带有MEM_RESERVE 标志的VirtualAlloc 来修复,然后两次调用MapViewOfFileEx,传递VirtualAlloc 找到的单个地址范围的两半。
猜你喜欢
  • 2014-01-20
  • 2021-11-26
  • 2012-04-04
  • 1970-01-01
  • 2018-10-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多