C#和VirtualAlloc,保留页面

C#和VirtualAlloc,保留页面,c#,.net,winapi,C#,.net,Winapi,我正在探索使用Pinvoke和手动管理一些大型缓冲区是否有意义 具体来说,我想保留虚拟页,这样我就可以通过提交下一个连续的虚拟页来增加数组,通过提交下一个连续的虚拟页来增加临时内存堆栈,将页设置为只读,等等。我应该知道有什么问题吗?比如,NET的正常分配是否会对该方案产生潜在干扰 编辑: 因为人们认为这太宽泛了,所以我特别关注: 我可以为VirtualAlloc保留哪些虚拟地址范围,以保证(ish)不会干扰.NET运行时的内存页?例如,一种常见的分配方案是将堆栈放在地址空间的顶部/底部,将堆放在

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

具体来说,我想保留虚拟页,这样我就可以通过提交下一个连续的虚拟页来增加数组,通过提交下一个连续的虚拟页来增加临时内存堆栈,将页设置为只读,等等。我应该知道有什么问题吗?比如,NET的正常分配是否会对该方案产生潜在干扰

编辑:

因为人们认为这太宽泛了,所以我特别关注:


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

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

对于与操作系统的接口,手动分配的缓冲区具有不移动和4kb对齐的优点。它可以直接用于调用无缓冲I/O函数

这里有一些代码。我写这篇文章已经很久了(尽管它还在使用),所以不要问我细节(即“比最初承诺的多5%”应该有什么好处…)。这个代码是在托管C++中运行的。p>
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;
}
试试看
{
LPVOID lpvReserveBase;//测试内存的基址
LPVOID lpvCommitBase;//测试内存的基址
SYSTEM\u INFO sSysInfo;//有关系统的有用信息
DWORD dwPageSize;//此计算机上的页面大小
GetSystemInfo(&sSysInfo);//初始化结构
dwPageSize=sSysInfo.dwPageSize;
ULONG nCommitPages=nTargetUserSizeBytes/dwPageSize;
if(nCommitPages*dwPageSize<(DWORD)nTargetUserSizeBytes)
{
nCommitPages++;
}
nTargetUserSizeBytes=nCommitPages*dwPageSize;
ULONG nReservedPages=(ULONG)(nCommitPages*1.05);//保留比最初承诺的多5%的空间
//在进程的虚拟地址空间中保留页。
lpvReserveBase=VirtualAlloc(
NULL,//系统选择地址
nReservedPages*dwPageSize,//分配的大小
MEM_RESERVE,//分配保留页
PAGE_NOACCESS);//保护=无访问权限
if(lpvReserveBase==NULL)
{
ErrLog(E_SHIF_ERR_DMAALLOC_失败,“错误:虚拟保留失败”);
返回FALSE;
}
//提交另一页。
lpvCommitBase=虚拟语言(
(LPVOID)lpvReserveBase,//要提交的下一页
nCommitPages*dwPageSize,//页面大小,以字节为单位
MEM_COMMIT,//分配一个已提交的页面
PAGE_READWRITE);//读/写访问
if(lpvCommitBase==NULL)
{
ErrLog(E_SHIF_ERR_DMAALLOC_失败,“错误:VirtualAlloc失败”);
返回FALSE;
}
m_pUserAddr=(LPTSTR)lpvCommitBase;
pDMADesc->m_DWReservedUserize=nReservedPages*dwPageSize;
}
捕获(标准::错误分配*exc)
{
sprintf_s(gsSH_IF_LastErr,512,“错误:AddDMA:分配std内存失败(%d字节,%s)”,nTargetUserSizeBytes,exc->what();
错误日志(E_SHIF_ERR_DMAALLOC_失败,gsSH_IF_LastErr);
返回FALSE;
}

如果你想做这样的事情,你可能不应该从使用C开始。使用非托管内存将使您无法使用该语言的大部分功能。此外,在绝大多数情况下,绝大多数人都不会从这种细粒度的内存管理中受益,最终会导致代码被破坏,而对于少数几个能够正常工作的人来说,他们中的大多数人将看到相当甚至更差的性能。这是一个好主意的可能性非常小。手动管理内存并不是那么难,特别是对于您希望在整个进程生命周期中都存在的大型缓冲区。将缓冲区包装在一个不安全的类中,可以让您将它们毫无问题地公开给代码库的其余部分,因此它不会“妨碍您使用该语言的大部分功能”。那么,业绩究竟会如何变得更糟呢?如果你的性能被分配和解除的瓶颈所困扰,那么,不管怎样,你可以做一些错误的事情。)):“如果你的工作被分配和解除了瓶颈,你就做了一些错误的事情。”——我想这可以很好地回答你自己的问题。我是一个C++游戏程序员。我非常清楚手动管理内存的所有问题和好处。我不是说要这样做所有甚至大部分的分配。我说的是在进程的生命周期中存在的一些非常大的缓冲区..Net使用的是唯一且相同的底层OS内存管理-那么您在寻找什么样的差异呢?很高兴我没有完全发疯:D您是如何分配缓冲区的?使用VirtualAlloc或HeapAlloc或其他什么?我回到办公室后会检查您。添加了一些代码片段,或多或少是代码中的1:1。谢谢!看起来您正在让操作系统决定页面的地址,但至少我现在知道.NET运行时不会干扰/兼容正常的Windows分配系统。