Как отладить или исправить проблему бесконечного цикла и повреждения кучи, связанную с boost :: interprocess managed_shared_memory?

У меня есть следующее сообщение «исключение первого шанса», которое исходит из написанной мной библиотеки DLL, которая выполняется внутри исполняемого файла, который я не писал. То есть DLL - это плагин. Когда это исключение возникает в первый раз, попытка открыть файл карты общей памяти завершается ошибкой. Если я игнорирую исключения первого шанса и просто запускаю, приложение в конечном итоге зависает или вылетает.

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 исключение первого шанса не генерируется и не идентифицируется как место, откуда оно появилось, потому что компилятор Visual C ++ 2008 не может найти сложный код шаблонов с расширенным вкусом. обсуждаемый. Однако, пройдя один раз через представление на языке ассемблера, я обнаружил, что код взрывается.

Строка верхнего уровня моего собственного кода, в которой все начинает портиться:

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

Вышеупомянутый класс managed_shared_memory взят из interprocess_fwd.hpp и является стандартной частью API / заголовков совместной памяти boost. Поскольку это основано на шаблоне, приведенное выше расширяется примерно до 2Kchars-длинного выражения шаблона Boost C ++, которое усекается на разную длину компоновщиком и отладчиком. 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::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)
   {}  

В любом случае, не пытайтесь отлаживать это дома, дети, вот что происходит:

введите описание изображения здесь

Наконец, используя мою ниндзя-подобную способность одиночного перехода через несколько миллионов строк на языке ассемблера, я победил злые ограничения отладчика Visual C ++ 2008 и нашел рассматриваемый код.

Вот что взрывается на самом деле: 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();
         }
      }

person Community    schedule 20.05.2013    source источник
comment
+1 за сварливое лицо от руки.   -  person John Dibling    schedule 20.05.2013
comment
Здесь есть что усвоить, но учли ли вы возможность того, что исключение «первый шанс» может быть совершенно безобидным?   -  person John Dibling    schedule 20.05.2013
comment
Я внес некоторые правки и удалил то, что было очень непрофессиональным в этом вопросе. Я прошу прощения за всех, кто прочитал оригинальную версию.   -  person Warren P    schedule 02.06.2013


Ответы (2)


Я считаю, что у меня были те же проблемы, что и у вас. Взгляните на функцию «shared_memory_object :: priv_open_or_create» в «\ boost \ interprocess \ shared_memory_object.hpp». В верхней части этой функции находится другой вызов функции «create_tmp_and_clean_old_and_get_filename», который запускает цепочку функций, завершающую удаление файла общей памяти. Я переместил этот вызов функции ниже в функции priv_open_or_create, где начинаются операторы case. Я считаю, что использую повышение 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;
}

Кстати, если кто-то знает официальные каналы, через которые я могу пройти, чтобы попытаться проверить это и добавить для повышения, пожалуйста, дайте мне знать, поскольку я ненавижу изменять такие вещи, не зная их полного эффекта.

Надеюсь это поможет!

person Community    schedule 23.10.2013
comment
Я думаю, что список рассылки boost был бы хорошим местом для публикации этого, поскольку автор Boost :: Interprocess мог бы сказать, будет ли это хорошим общим исправлением. - person Warren P; 25.10.2013

Boost полон как удивительных, так и пугающих вещей.

Простой обходной путь в Windows может заключаться в переключении на managed_windows_shared_memory вместо managed_shared_memory, вы можете решить множество неприятных проблем, связанных с сбоями / зависанием, и один вид сбоев / зависаний, по-видимому, вызван, в свою очередь, различиями между файловой системой Windows. поведение и поведение файловой системы unix, и, в частности, кажется, что с ускорением и managed_shared_memory в Windows можно выйти из-под ограничений блокировки файловой системы Windows. Мне сообщили, что попытка решить эту проблему была завершена в BOost 1.53, но я использую Boost 1.53, и у меня все еще есть эта проблема.

С обычным managed_shared_memory в Windows вы получите настойчивость вне срока службы любого клиентского или серверного приложения. В некоторых случаях это может быть желательно, поэтому обходной путь не является настоящим решением для этих людей.

Однако в моем случае мне это все равно не нужно, хотя я думал, что это будет удобно, но оказалось, что это причиняет больше боли, чем стоит, по крайней мере, с текущей реализацией Boost в Windows.

Я хотел бы также отметить, что удаление файла общей памяти, по-видимому, является основной причиной состояния гонки, которое вызывает проблему, возникшую в вопросе выше. Правильная синхронизация создания, проверки и удаления файла, по-видимому, имеет важное значение для реальной реализации системы, и, в частности, это представляется серьезной проблемой, если ваш главный (сервер) удаляет файл с общей памятью. в то время как некоторые клиенты все еще используют его. По-видимому, необходима перезагрузка, чтобы очистить возникшую блокировку + беспорядок в файловой системе NTFS.

Если я найду реальное решение, я опубликую его, но это больше информации, чем я мог бы найти где-либо еще. Остерегайтесь managed_shared_memory, подумайте об использовании managed_windows_shared_memory и забудьте о попытках заставить работать идею «постоянной общей памяти». Лучше использовать непостоянные только окна managed_windows_shared_memory.

Решение этой проблемы при сохранении класса managed_shared_memory в моем приложении, вероятно, означает перенос всего доступа к объекту managed_shared_memory в еще один уровень примитивов межпроцессной синхронизации или даже с необработанным мьютексом Win32 API. Boost может сделать что-то эквивалентное, но, вероятно, внесет еще больше случайных сложностей.

(В сторону: я единственный здесь, кто считает, что Template-All-the-things в наши дни зашел слишком далеко в обычном использовании, особенно в Boost?)

Обновление 2: я нашел альтернативный способ зависания managed_shared_memory, который, в свою очередь, приводит к зависанию любого приложения, из которого вы его используете. Я не ожидал, что с помощью Boost будет так легко создавать тупиковые ситуации, но это довольно легко сделать. Код мьютекса внутри реализации зависнет навсегда, ожидая мьютекса, который другой пользователь управляемой разделяемой памяти ушел, не освободив. Этот бесконечный сон в ожидании мьютекса, который никогда не будет выпущен, является еще одним серьезным недостатком дизайна в этой реализации межпроцессного ускорения, в котором до сих пор я насчитал несколько серьезных недостатков дизайна, по крайней мере, в Windows. Может, в Линуксе прекрасно работает.

Код, демонстрирующий это, - это метод 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++
person Warren P    schedule 26.05.2013
comment
Вы случайно не исправили обновление 2 часть ?? Мой потребительский код будет бездействовать на неопределенный срок, когда я использую этот MyDeque * Mydeque = segment.find ‹MyDeque› (dequeName) .first; - person PrimeOfKnights; 23.08.2017
comment
Неа. Я полагал, что любой код, который так грамотно написан, что я не могу его прочитать, не является правильным решением в моей кодовой базе. Перешел на что-то более глупое и менее трудное для чтения. DDE. Ага. DDE. В 20хх. - person Warren P; 24.08.2017
comment
Я не уверен, пробовали ли вы это. Мое первое приложение было построено на 64-битной системе с использованием Visual Studio, а мое потребительское приложение - на 32-битной. Я изменил оба на 64 билда, и теперь он работает. Надеюсь, вы сочтете это полезным. - person PrimeOfKnights; 24.08.2017