C#和VirtualAlloc,保留页面

时间:2014-04-08 18:28:18

标签: c# .net winapi

我正在探索使用Pinvoke和VirtualAlloc手动管理一些大缓冲区是否有意义。

具体来说,我想保留虚拟页面,这样我就可以通过提交下一个连续的虚拟页面来增加数组,拥有可以通过提交下一个连续虚拟页面来增长的临时内存堆栈,设置一个页面以供读取只有等等。我应该注意哪些问题?比如,NET的正常分配是否可能会干扰这种方案?

编辑:

由于人们认为这太宽泛,所以我特别关注这个问题:

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

1 个答案:

答案 0 :(得分:2)

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

为了与操作系统连接,手动分配的缓冲区具有不移动和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;
}