我正在探索使用Pinvoke和VirtualAlloc手动管理一些大缓冲区是否有意义。
具体来说,我想保留虚拟页面,这样我就可以通过提交下一个连续的虚拟页面来增加数组,拥有可以通过提交下一个连续虚拟页面来增长的临时内存堆栈,设置一个页面以供读取只有等等。我应该注意哪些问题?比如,NET的正常分配是否可能会干扰这种方案?
编辑:
由于人们认为这太宽泛,所以我特别关注这个问题:
我可以保证VirtualAlloc可以保留哪些虚拟地址范围(ish)不会干扰.NET运行时的内存页面?例如,一种常见的分配方案是将堆栈放置在地址空间的顶部/底部,堆放在地址空间的底部/顶部,并让它们朝向彼此生长。如果.NET运行时正在执行此操作,我应该可以从地址范围的中间保留页面。
答案 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;
}