在C++应用程序中安全地扩展内存映射文件

3
我有一个古老的C++应用程序,最初是在Visual C++ 6.0中构建的,它使用一个非常复杂的共享内存DLL在大约8个EXE和DLL之间共享数据,这些文件都具有可被一个或两个字符串字典替换的值池用作键,并且记录用作值。该应用程序是多线程和多进程的。有三个主要的可执行文件读写共享内存区域,几个可执行文件有3个或更多线程读/写或“排队”信息到这个共享内存区域。大约有几百个地方,使用__try__except的结构化异常处理(SEH)来过滤异常,并尝试通过调整由称为CGMMF的类管理的段的大小来处理访问冲突。这意味着可增长的内存映射文件。
因为我找不到任何有关这种技术的连贯文档,以及它的安全性和适用性,所以在这里显示了最显著的细节。实验表明,这个库在1998年的单核系统上并没有很好地工作,在运行Windows XP的单核虚拟机上有些效果,在2013年的现代2+ GHz多核Windows 7 64位系统上根本不起作用。我正在尝试修复或替换它。
#define ResAddrSpace(pvAddress, dwSize)  \
   (m_hFileMapRes = CreateFileMapping(HFILE_PAGEFILE, &m_SecAttr,             \
      PAGE_READWRITE| SEC_RESERVE, 0, dwSize, m_szRegionName),                    \
   (m_hFileMapRes == NULL) ? NULL :                                     \
      MapViewOfFileEx(m_hFileMapRes, FILE_MAP_ALL_ACCESS, 0, 0, dwSize, 0))


void CGmmf::Create(void)
{
    DWORD dwMaxRgnSize;
    if (Gsinf.dwAllocationGranularity == 0) 
    {
        GetSystemInfo(&Gsinf);
    }
    m_dwFileSizeMax = RoundUp(m_dwFileSizeMax, Gsinf.dwAllocationGranularity);
    m_dwFileGrowInc = RoundUp(m_dwFileGrowInc, Gsinf.dwAllocationGranularity);
    dwMaxRgnSize = m_dwFileSizeMax + m_dwOverrunBuf;
    m_pbFile = (PBYTE)ResAddrSpace(NULL, dwMaxRgnSize);
        Adjust(m_dwFileSizeNow);
}

void CGmmf::Adjust(IN DWORD dwDiskFileNow) 
{
    int nThreadPriority;

    __try 
    {
        //
        // Boost our thread's priority so that another thread is 
        // less likely to use the same address space while 
        //  we're changing it.
        //
        nThreadPriority = GetThreadPriority(GetCurrentThread());
        SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);

        //
        // Restore the contents with the properly adjusted lengths
        //
        Construct(dwDiskFileNow);
    }
    __finally 
    {       
        //
        // Make sure that we always restore our priority class and thread 
        // priority so that we do not continue to adversely affect other 
        // threads in the system.
        //

        SetThreadPriority(GetCurrentThread(), nThreadPriority);
    }
}


void CGmmf::Construct(IN DWORD dwDiskFileNow) 
{
    DWORD dwDiskFileNew = RoundUp(dwDiskFileNow, m_dwFileGrowInc),
          dwStatus = ERROR_SUCCESS;

    PBYTE pbTemp;

    if (dwDiskFileNew > 0) 
    {
        //
        // Grow the MMF by creating a new file-mapping object.
        //
            // use VirtualAlloc() here to commit
        // the requested memory: VirtualAlloc will not fail
        // even if the memory block is already committed:

        pbTemp = (PBYTE)VirtualAlloc(m_pbFile,dwDiskFileNew,MEM_COMMIT,PAGE_READWRITE);

        if(NULL == pbTemp)
        {
            LogError(GetLastError(), MEM_CREATE_MMF, m_szRegionName);

            // 
            //  File-mapping could not be created, the disk is 
            //  probably full.
            //
            RaiseException(EXCEPTION_GMMF_DISKFULL, 
                           EXCEPTION_NONCONTINUABLE, 
                           0, 
                           NULL);
        }

        // 
        //  Check to see if our region has been corrupted 
        //  by another thread.
        //
        if (pbTemp != m_pbFile)
        {
            RaiseException(EXCEPTION_GMMF_CORRUPTEDRGN, 
                           EXCEPTION_NONCONTINUABLE, 
                           0, 
                           NULL);
        }
    }
}

到目前为止,我替换它的选项包括尝试使用适当的DCOM(进程外COM)和COM(进程内COM)替换所有共享内存,并手动防止并发问题,使用同步/互斥/临界区或其他线程安全结构。我想知道是否已经有一些线程安全的内存字典类型可以替换所有这些。即使在上面的代码片段中,它只是这个古老的Visual C++ 6共享内存库的不到1%的代码,也有让我感到恐惧的事情。例如,提高线程优先级作为避免死锁、竞争条件和一般损坏的策略。也许在80486 CPU的33 MHz上,这样做能让这个代码停止崩溃。我已经在Visual C++ 6.0中构建和运行了代码,也有一个分支在Visual C++ 2008中运行,我可能还可以在Visual C++ 2010中运行它。我应该使用什么来给我字典语义,跨进程共享内存,并且稳定可靠? 更新:当我提到“dictionary”时,我指的是Python中已知的dictionary数据类型,有些地方也称为“键/值存储”,在其他地方(如C++标准库)中则被称为std::map。讨论这个问题的Boost文档在这里

这个尝试值得点赞。 - SChepurin
1
有没有什么理由不使用带有CreateMutex同步的“真正”的MapViewOfFile共享内存? - paulm
如何帮助我创建一个内存映射文件? - Warren P
1个回答

1

看起来你应该看一下Boost Interprocess。你可以使用它在共享内存中拥有类似于std::map的对象等等。我已经好多年没用过它了,所以不能详细解释,但是库的文档很好,有大量的示例,应该可以在30分钟内让你上手。


Boost Interprocess看起来不错。虽然文档代码示例可以再好一些:https://dev59.com/Lm_Xa4cB1Zd3GeqP1HAL#15315088 - Warren P
@WarrenP:嗯,它是免费和开源的,很难抱怨。我相信你的贡献会受到赞赏 :) - user405725
在Windows上,它似乎使用文件而不是CreateMutex,因此上次我使用它时性能相当差。 - paulm

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接