2014-04-08 60 views
0

我在探索是否有意义使用Pinvoke和VirtualAlloc来手动管理一些大型缓冲区。C#和VirtualAlloc,保留页面

具体来说,我想订虚拟页这样我就可以通过提交下一个连续的虚拟页面做的事情一样成长阵列,有一个临时存储器堆叠,可以通过提交下一个连续虚拟页长,设置页面只读,等。我应该知道的任何陷阱?就像,NET的正常分配可能会干扰这种方案?

编辑:

因为人们认为这是过于宽泛,这是我关心具体是:

我可以与保证(ISH)不与.NET运行时的干扰的VirtualAlloc保留哪些虚拟地址范围内存页面?例如,一种常见的分配方案是将堆栈放置在地址空间的顶部/底部,并将堆放置在地址空间的底部/顶部,并使它们朝向彼此成长。如果.NET运行时正在这样做,我应该可以保留地址范围中间的页面。

+3

如果你想做这样的事情,你可能不应该使用C#开始。使用非托管内存将阻止您使用该语言的大部分功能。另外绝大多数情况下绝大多数人都不会从这种细粒度的内存管理中受益,并且最终会导致代码被破坏,对于那些能够正常工作的极少数人来说,大多数*他们*将会看到相当甚至更糟糕的表现。这是一个好主意的可能性是微乎其微的。 – Servy

+2

手动管理内存不是很难,特别是对于您希望在整个流程生命周期中存在的大型缓冲区。将缓冲区包装在不安全的类中可让您将它们暴露给代码库的其余部分,因此它不会“妨碍您使用大部分语言的功能”。而地球上的表现会变得更糟?如果通过分配和释放你的表现是瓶颈的,那么无论如何你都会做错事。 –

+1

:)“如果通过分配和释放你的表现是瓶颈的,那么无论如何你都会做错事。” - 我认为这很好地回答了你自己的问题。 –

回答

2

我一直在做我们的大型项目中的这一切。大内存用作将大量数据写入磁盘(来自传感器硬件)的应用程序的循环缓冲区。访问缓冲区的代码是用托管C++编写的,因为它更容易访问非托管函数,但我认为一个不安全的C#代码块也可以很好地工作。我不记得有任何问题。

为了与OS接口,手动分配的缓冲区具有不移动和4kb对齐的优点。它可以直接用于调用无缓冲的I/O功能。

这是一段代码。我写这篇文章的时间很长(虽然它仍在使用中),所以不要问我关于细节的问题(比“最初承诺的5%”应该对......有好处)。此代码正在托管的C++中运行。

try 
{ 

    LPVOID lpvReserveBase;    // base address of the test memory 
    LPVOID lpvCommitBase;    // base address of the test memory 
    SYSTEM_INFO sSysInfo;   // useful information about the system 
    DWORD dwPageSize;    // the page size on this computer 

    GetSystemInfo(&sSysInfo);  // initialize the structure 
    dwPageSize = sSysInfo.dwPageSize; 

    ULONG nCommitPages = nTargetUserSizeBytes/dwPageSize; 
    if (nCommitPages * dwPageSize < (DWORD)nTargetUserSizeBytes) 
    { 
     nCommitPages++; 
    } 
    nTargetUserSizeBytes = nCommitPages * dwPageSize; 
    ULONG nReservedPages = (ULONG)(nCommitPages * 1.05); // reserve 5% more than initially committed 

    // Reserve pages in the process's virtual address space. 
    lpvReserveBase = VirtualAlloc(
     NULL,    // system selects address 
     nReservedPages * dwPageSize, // size of allocation 
     MEM_RESERVE,  // allocate reserved pages 
     PAGE_NOACCESS);  // protection = no access 
    if (lpvReserveBase == NULL) 
    { 
     ErrLog(E_SHIF_ERR_DMAALLOC_FAILED, "Error: VirtualAlloc reserve failed"); 
     return FALSE; 
    } 

    // commit another page. 
    lpvCommitBase = VirtualAlloc(
     (LPVOID)lpvReserveBase, // next page to commit 
     nCommitPages * dwPageSize, // page size, in bytes 
     MEM_COMMIT,  // allocate a committed page 
     PAGE_READWRITE); // read/write access 
    if (lpvCommitBase == NULL) 
    { 
      ErrLog(E_SHIF_ERR_DMAALLOC_FAILED, "Error: VirtualAlloc failed"); 
      return FALSE; 
    } 

    m_pUserAddr = (LPTSTR)lpvCommitBase; 
    pDMADesc->m_dwReservedUserSize = nReservedPages * dwPageSize; 
} 
catch (std::bad_alloc* exc) 
{ 

    sprintf_s(gsSH_IF_LastErr, 512, "Error: AddDMA: Failed allocate std memory (%d bytes, %s)", nTargetUserSizeBytes, exc->what()); 
    ErrLog(E_SHIF_ERR_DMAALLOC_FAILED, gsSH_IF_LastErr); 
    return FALSE; 
} 
+0

很高兴我并不完全疯狂,那么:D你是如何分配缓冲区的?使用VirtualAlloc或HeapAlloc或其他东西? –

+0

一旦我回到办公室,我会检查你。 – PMF

+1

添加了一些代码片段,它在我们的代码中出现或多或少为1:1。 – PMF