2013-05-20 129 views
9

我有下面的「一次機會異常」消息,它來自我寫的一個DLL,它運行在我沒有寫入的可執行文件中。也就是說,DLL是一個插件。這個異常第一次觸發時,嘗試打開共享內存映射文件失敗。如果我忽略第一次機會異常並運行,應用程序最終會凍結或崩潰。如何調試或修復無盡循環和堆腐敗問題,涉及boost :: interprocess managed_shared_memory?

First-chance exception at 0x76a7c41f in notmyexe.exe: Microsoft C++ exception: boost::interprocess::interprocess_exception at memory location 0x002bc644.. 

幾個小時後,它似乎是由被不斷地循環,直到預期的異常條件清除的代碼塊引起的。事實證明,如果它永遠不會清除,那麼最終,這個異常會變成另一個低級異常條件和/或變成堆損壞。所有這些只是爲了使用Boost :: interprocess打開共享內存區域。

使事情複雜化的第一件事是,在我基於Visual C++ 2008的項目中,第一個機會異常不會被拋出並被識別爲它的來源,因爲Visual C++ 2008編譯器無法找到複雜的提升有問題的味道模板代碼。然而,通過單步執行彙編語言視圖,我發現了代碼爆炸。

的我自己的代碼的頂層線,這一切都開始走壞的是:

segment = new managed_shared_memory( open_or_create 
             , MEMORY_AREA_NAME 
             , SHARED_AREA_SIZE);   

以上managed_shared_memory類是interprocess_fwd.hpp,並且大於助推共享內存API標準的一部分/頭。因爲它是基於模板的,所以上面擴展爲大約2Kchars長的C++ boost模板表達式,它由鏈接器和調試器以不同的長度截斷。 Visual C++ 2008沒有更多的源代碼調試功能,看起來這些限制在起作用。

例如,當它炸燬我得到這個調用堆棧:

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代碼是什麼,爲什麼它必須無止境地循環?

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... 

而且層下來,我們得到:

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) 
    {} 

不管怎麼說,不要試圖在家裏的孩子調試此,這裏是發生了什麼:

enter image description here

最後,使用我的忍者式功能單步執行數百萬行彙編語言我已經擊敗了Visual C++ 2008的惡意調試器限制,並且已經找到了代碼estion。

這是事實上吹起來的東西:create_device<FileBased>(dev...

一些背景這裏: managed_open_or_create_impl.h線351 ...

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(); 
     } 
     } 
+1

+1爲寫意的脾氣暴躁的臉。 –

+0

這裏有很多東西需要吸收,但是你有沒有考慮過First-Chance異常可能完全沒有問題的可能性? –

+0

我做了一些編輯並刪除了這個問題中的一些非常不專業的東西。我爲任何必須閱讀原始版本的人道歉。 –

回答

2

我相信我已經有一些相同的問題,你有。看看「\ boost \ interprocess \ shared_memory_object.hpp」中的「shared_memory_object :: priv_open_or_create」函數。在該函數的頂部是另一個函數調用「create_tmp_and_clean_old_and_get_filename」,它啓動一個功能鏈,最終刪除共享內存文件。我最終在case語句開始處的priv_open_or_create函數中移動該函數調用較低的位置。我相信我正在使用提升1.48。下面是一個函數,我修改的最終版本:

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; 
} 

順便說一句,如果有人知道官方渠道我可以順利通過,試圖讓這個驗證並加入到促進請讓我知道,因爲我討厭修改類的東西這不知道它的全部效果。

希望這會有所幫助!

+0

我認爲boost boost郵件列表將是一個很好的地方發佈這個作爲Boost :: Interprocess的作者可以說這是否是一個很好的一般性修復。 –

0

升壓充滿了既令人驚歎和可怕的事情。

在Windows上簡單的解決方法,可以切換到managed_windows_shared_memory而不是managed_shared_memory,可以解決各種討厭的崩潰/掛起的問題,以及一個那種崩潰/掛起問題似乎由不同所導致,反過來在Windows文件系統行爲和unix文件系統行爲之間,特別是在Windows上使用boost和managed_shared_memory似乎可能違反Windows文件系統鎖定限制。我被告知,在1.53版本中已經完成了處理這個問題的工作,但是我使用了Boost 1.53,並且仍然存在這個問題。

在Windows上定期使用managed_shared_memory,可以在任何客戶端或服務器應用程序的生命週期內獲得持久性。這在某些人的情況下可能是可取的,因此解決方法對於這些人來說不是真正的解決方案。但是,就我而言,我並不真的需要這樣做,儘管我認爲它會很方便,但結果是比它的價值更加痛苦,至少在Windows上使用當前的Boost實現。

我想指出,刪除共享內存文件似乎是導致上述問題中遇到的問題的競爭條件的根本原因。圍繞創建和檢查進行適當的同步以及刪除文件對於系統的真實世界實施似乎是必不可少的,特別是,如果您的主服務器(主服務器)刪除了共享內存文件,它似乎是一個破壞性問題而一些客戶仍在使用它。重新啓動似乎需要清除由此產生的鎖+ NTFS文件系統混亂。

如果我找到一個真正的解決方案,我會發布它,但以上是比我能找到的其他地方更多的信息。警惕managed_shared_memory並考慮使用managed_windows_shared_memory,並忘記試圖讓「持久共享內存」的想法工作。而是使用非持久性窗口 - 僅限於managed_windows_shared_memory

解決這個,同時保持managed_shared_memory類在我的應用程序可能意味着在包間同步原語,甚至與原始的Win32 API互斥的另一個級別的所有訪問managed_shared_memory對象。 Boost可以做同樣的事情,但可能會引入更多的意外複雜性。

(題外話:我是這裏唯一一個誰認爲模板的所有最事情已經普遍使用走得太遠,特別是在加速,這幾天?)

更新2:我找到了凍結managed_shared_memory的另一種方法,這反過來又凍結了你使用它的任何應用程序。我沒有想到用Boost造成死鎖是很容易的,但它很容易做到。實現中的互斥體代碼將永久凍結,等待互斥體,其中一些管理共享內存的其他用戶已經消失而不釋放。這種無休止的睡眠等待着一個永遠不會被釋放的互斥體,這是這種推動進程間實現的另一個深層設計缺陷,迄今爲止,我已經計算出了幾個嚴重的設計缺陷,至少在Windows中。也許它在Linux上很漂亮。

顯示出這是find()方法的代碼,這樣調用:

boost::interprocess::managed_shared_memory * segment; 
    std::pair<MyType*, std::size_t> f = segment->find<MyType>(name); 

這裏是互斥死鎖(又名無盡的等待,冷凍的任務)堆棧跟蹤:

只有解決方案是在停止或終止所有等待此互斥鎖的掛起進程後,刪除共享內存區域。

> 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

Didyou有任何機會修復更新2部分?我的消費者代碼將無限期睡眠,當我使用這個 MyDeque * Mydeque = segment.find (「dequeName」)。 – brownKnight

+0

不是。我認爲任何編寫得如此巧妙以至於無法閱讀的代碼在我的代碼庫中都不是正確的解決方案。切換到一些笨重的,不太難以閱讀的。 DDE。是的。 DDE。在20xx。 –

+0

我不確定您是否嘗試過這種方式。我的第一個應用程序使用Visual Studio構建在64位上,我的使用應用程序位於32位上。我改變了兩個64版本,現在它的作品。我希望你覺得這對你有幫助。 – brownKnight