首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何调试或修复涉及boost::interprocess managed_shared_memory的无限循环和堆损坏问题?

如何调试或修复涉及boost::interprocess managed_shared_memory的无限循环和堆损坏问题?
EN

Stack Overflow用户
提问于 2013-05-20 22:36:36
回答 2查看 2.5K关注 0票数 10

我有下面的“第一次机会异常”消息,它来自我编写的DLL,该DLL在我没有编写的可执行文件中运行。也就是说,DLL是一个插件。

代码语言:javascript
复制
First-chance exception at 0x76a7c41f in notmyexe.exe: Microsoft C++ exception: boost::interprocess::interprocess_exception at memory location 0x002bc644..

几个小时后,它似乎是由一个代码块引起的,该代码块不断循环,直到预期的异常条件清除。事实证明,如果它从来没有清除,然后,最终,这个异常会变成另一个低级别的异常条件和/或变成堆损坏。所有这些都只是为了使用Boost::interprocess打开共享内存区。

使事情复杂化的第一件事是,在我的基于Visual C++ 2008的项目中,第一个boost::interprocess::interprocess_exception first-chance异常没有被抛出,并且被标识为它来自的位置,因为Visual C++ 2008编译器找不到有问题的复杂boost风格的模板代码。然而,通过在汇编语言视图中单步执行,我发现了会爆炸的代码。

在我自己的代码中,最顶层的一行代码开始变得糟糕:

代码语言:javascript
复制
  segment = new managed_shared_memory(   open_or_create
                                      ,  MEMORY_AREA_NAME
                                      , SHARED_AREA_SIZE );          

上面的managed_shared_memory类来自interprocess_fwd.hpp,是boost共享内存API/头的标准部分。因为它是基于模板的,所以上面的表达式扩展为大约2Kchars长的C++ boost模板表达式,链接器和调试器在不同的长度截断该表达式。Visual C++ 2008没有更多的源代码调试功能,这些限制似乎在起作用。

例如,当它爆炸时,我得到这个调用堆栈:

代码语言:javascript
复制
    KernelBase.dll!76a7c41f()   
    [Frames below may be incorrect and/or missing, no symbols loaded for KernelBase.dll]    
    KernelBase.dll!76a7c41f()   
>   msvcr90d.dll!_malloc_dbg(unsigned int nSize=2290875461, int nBlockUse=264, const char * szFileName=0x01fcb983, int nLine=1962999808)  Line 160 + 0x1b bytes C++
    8bfc4d89()  

在上面的堆栈转储中没有出现实际的最终用户编写的源函数。

我应该如何调试它?其次,在Visual C++ 2008中,boost-interprocess有什么已知的问题吗?第三,下面的boost代码在做什么,为什么它必须无休止地循环?

代码语言:javascript
复制
boost::interprocess::basic_managed_shared_memory<char,
   boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,
        boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,
        boost::interprocess::iset_index>::basic_managed_shared_memory<char,boo...

再往下一层,我们会得到:

代码语言:javascript
复制
basic_managed_shared_memory (open_or_create_t,
                              const char *name, size_type size,
                              const void *addr = 0, const permissions& perm = permissions())
      : base_t()
      , base2_t(open_or_create, name, size, read_write, addr,
                create_open_func_t(get_this_pointer(),
                ipcdetail::DoOpenOrCreate), perm)
   {}  

不管怎样,孩子们,不要试图在家里调试它,下面是发生的事情:

最后,使用我的忍者般的能力在数百万行汇编语言中单步执行,我克服了Visual C++ 2008的邪恶调试器限制,并找到了有问题的代码。

这里有一些上下文: managed_open_or_create_impl.h第351行...

代码语言:javascript
复制
else if(type == DoOpenOrCreate){
         //This loop is very ugly, but brute force is sometimes better
         //than diplomacy. If someone knows how to open or create a
         //file and know if we have really created it or just open it
         //drop me a e-mail!
         bool completed = false;
         while(!completed){
            try{
               create_device<FileBased>(dev, id, size, perm, file_like_t()); // <-- KABOOM!
               created     = true;
               completed   = true;
            }
            catch(interprocess_exception &ex){
               if(ex.get_error_code() != already_exists_error){
                  throw;
               }
               else{
                  try{
                     DeviceAbstraction tmp(open_only, id, read_write);
                     dev.swap(tmp);
                     created     = false;
                     completed   = true;
                  }
                  catch(interprocess_exception &e){
                     if(e.get_error_code() != not_found_error){
                        throw;
                     }
                  }
                  catch(...){
                     throw;
                  }
               }
            }
            catch(...){
               throw;
            }
            thread_yield();
         }
      }
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2013-10-24 02:17:42

我相信我遇到了一些和你一样的问题。看看"\boost\interprocess\shared_memory_object.hpp".中的函数"shared_memory_object::priv_open_or_create“在该函数的顶部是另一个名为"create_tmp_and_clean_old_and_get_filename“的函数,该函数启动一个函数链,最终删除共享内存文件。我最终在priv_open_or_create函数中将该函数调用移到了case语句开始的位置。我相信我使用的是boost 1.48。下面是我修改过的函数的最终版本:

代码语言:javascript
复制
inline bool shared_memory_object::priv_open_or_create
   (ipcdetail::create_enum_t type, const char *filename, mode_t mode, const permissions &perm)
{
   m_filename = filename;
   std::string shmfile;
   std::string root_tmp_name;

   //Set accesses
   if (mode != read_write && mode != read_only){
      error_info err = other_error;
      throw interprocess_exception(err);
   }

   switch(type){
      case ipcdetail::DoOpen:
            ipcdetail::get_tmp_base_dir(root_tmp_name);
            shmfile = root_tmp_name;
            shmfile += "/";
            shmfile += filename;
            m_handle = ipcdetail::open_existing_file(shmfile.c_str(), mode, true);
      break;
      case ipcdetail::DoCreate:
            ipcdetail::create_tmp_and_clean_old_and_get_filename(filename, shmfile);
          m_handle = ipcdetail::create_new_file(shmfile.c_str(), mode, perm, true);
      break;
      case ipcdetail::DoOpenOrCreate:
         ipcdetail::create_tmp_and_clean_old_and_get_filename(filename, shmfile);
          m_handle = ipcdetail::create_or_open_file(shmfile.c_str(), mode, perm, true);
      break;
      default:
         {
            error_info err = other_error;
            throw interprocess_exception(err);
         }
   }

   //Check for error
   if(m_handle == ipcdetail::invalid_file()){
      error_info err = system_error_code();
      this->priv_close();
      throw interprocess_exception(err);
   }

   m_mode = mode;
   return true;
}

顺便说一句,如果任何人知道官方渠道,我可以尝试得到这个验证和添加到boost,请让我知道,因为我讨厌修改这样的东西,而不知道它的全部效果。

希望这能有所帮助!

票数 2
EN

Stack Overflow用户

发布于 2013-05-27 03:03:24

Boost中既有令人惊叹的东西,也有可怕的东西。

一个简单的变通方法在Windows上,可以是切换到managed_windows_shared_memory而不是managed_shared_memory,您可以解决各种讨厌的崩溃/挂起问题,和一种崩溃/挂起问题似乎引起的,依次由Windows文件系统行为和unix文件系统行为之间的差异,特别是,似乎在Windows上的boost和managed_shared_memory,它可以运行与Windows文件系统锁定的限制冲突。我被告知,在BOost 1.53中已经完成了解决这个问题的努力,但我正在使用Boost1.53,我仍然存在这个问题。

使用Windows上的常规managed_shared_memory,您可以获得超出任何客户端或服务器应用程序生命周期的持久性。在某些人的情况下,这可能是可取的,因此解决方法对这些人来说并不是一个真正的修复方法。

然而,在我的情况下,无论如何,我并不真的需要它,尽管我认为它会很方便,但事实证明它比它的价值更痛苦,至少在Windows上目前的Boost实现。

我还想指出,删除共享内存文件似乎是导致上面问题中遇到的问题的竞态条件的根本原因。在创建、检查和删除文件的过程中,正确的同步似乎对系统的实际实现至关重要,尤其是,如果您让您的主(服务器)在某些客户端仍在使用共享内存文件时将其删除,则这将是一个灾难性的问题。重新启动似乎是必要的,以清除产生的lock+NTFS-文件系统混乱。

如果我找到了一个真正的解决方案,我会把它贴出来,但上面的信息比我能找到的任何其他地方都多。对managed_shared_memory保持警惕,考虑使用managed_windows_shared_memory,忘记尝试让“持久共享内存”的想法发挥作用。相反,应该使用非持久化的纯windows managed_windows_shared_memory

解决这个问题的同时,在我的应用程序中保留managed_shared_memory类可能意味着将对managed_shared_memory对象的所有访问包装在另一层进程间同步原语中,甚至使用原始的Win32 API互斥锁。Boost可以做一些等效的事情,但可能会引入更多的意外复杂性。

(旁白:我是这里唯一一个认为模板-所有的东西在一般使用中,特别是在Boost中使用得太多的人吗?)

更新2:我发现了一种冻结managed_shared_memory的替代方法,这反过来也会冻结你使用它的任何应用程序。我没想到用Boost创建死锁会这么容易,但它很容易做到。实现中的互斥代码将永远冻结,等待托管共享内存的其他用户离开而没有释放的互斥。这种等待互斥的无尽睡眠永远不会被释放,这是boost进程间实现中的另一个深层次的设计缺陷,到目前为止,我已经统计了几个严重的设计缺陷,至少在windows上是这样。也许它在Linux上运行得很好。

显示这一点的代码是find()方法,调用方式如下:

代码语言:javascript
复制
   boost::interprocess::managed_shared_memory * segment;
   std::pair<MyType*, std::size_t> f = segment->find<MyType>(name);

下面是互斥死锁(又称无尽等待,冻结任务)的堆栈跟踪:

当你到达这里,唯一的解决方案是在停止或杀死所有等待互斥锁的挂起进程后,删除共享内存区。

代码语言:javascript
复制
>   myapp.exe!boost::interprocess::winapi::sched_yield()  Line 998  C++
    myapp.exe!boost::interprocess::ipcdetail::thread_yield()  Line 60 + 0xe bytes   C++
    myapp.exe!boost::interprocess::ipcdetail::spin_mutex::lock()  Line 71   C++
    myapp.exe!boost::interprocess::ipcdetail::spin_recursive_mutex::lock()  Line 91 C++
    myapp.exe!boost::interprocess::interprocess_recursive_mutex::lock()  Line 161   C++
    myapp.exe!boost::interprocess::scoped_lock<boost::interprocess::interprocess_recursive_mutex>::lock()  Line 280 C++
    myapp.exe!boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,boost::interprocess::iset_index>::priv_get_lock(bool use_lock=true)  Line 1340   C++
    myapp.exe!boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,boost::interprocess::iset_index>::priv_generic_find<char>(const char * name=0x00394290, boost::interprocess::iset_index<boost::interprocess::ipcdetail::index_config<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0> > > & index={...}, boost::interprocess::ipcdetail::in_place_interface & table={...}, unsigned int & length=1343657312, boost::interprocess::ipcdetail::bool_<1> is_intrusive={...}, bool use_lock=true)  Line 854 + 0x11 bytes  C++
    myapp.exe!boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,boost::interprocess::iset_index>::priv_find_impl<boost::container::map<AreaKeyType,DATA_AREA_DESC,std::less<AreaKeyType>,boost::interprocess::allocator<std::pair<AreaKeyType const ,DATA_AREA_DESC>,boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,boost::interprocess::iset_index> > > >(const char * name=0x00394290, bool lock=true)  Line 728 + 0x25 bytes    C++
    myapp.exe!boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,boost::interprocess::iset_index>::find<boost::container::map<AreaKeyType,DATA_AREA_DESC,std::less<AreaKeyType>,boost::interprocess::allocator<std::pair<AreaKeyType const ,DATA_AREA_DESC>,boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,boost::interprocess::iset_index> > > >(const char * name=0x00394290)  Line 423 + 0x1e bytes  C++
    myapp.exe!boost::interprocess::ipcdetail::basic_managed_memory_impl<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,boost::interprocess::iset_index,8>::find<boost::container::map<AreaKeyType,DATA_AREA_DESC,std::less<AreaKeyType>,boost::interprocess::allocator<std::pair<AreaKeyType const ,DATA_AREA_DESC>,boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,boost::interprocess::iset_index> > > >(boost::interprocess::ipcdetail::char_ptr_holder<char> name={...})  Line 346 + 0x23 bytes   C++
    myapp.exe!boost::interprocess::basic_managed_shared_memory<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,boost::interprocess::iset_index>::find<boost::container::map<AreaKeyType,DATA_AREA_DESC,std::less<AreaKeyType>,boost::interprocess::allocator<std::pair<AreaKeyType const ,DATA_AREA_DESC>,boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,boost::interprocess::iset_index> > > >(boost::interprocess::ipcdetail::char_ptr_holder<char> name={...})  Line 208 + 0x10 bytes  C++
    myapp.exe!CCommonMemory::AllocateOrFindAreaMap(const char * name=0x00394290)  Line 128  C++
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/16651878

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档