2010-08-20 2 views
8

В настоящее время я работаю над системой отчетов об ошибках на основе исключений для приложений Windows MSVC++ (9.0) (т. Е. Структур исключения & типов/наследования, стека вызовов, сообщений об ошибках & и т. Д.).

Мой вопрос сейчас: как правильно сообщать & log out ошибка памяти?

При возникновении этой ошибки, например. как bad_alloc, брошенный оператором new, может быть много «функций» недоступно, в основном в отношении дальнейшего распределения памяти. Как правило, я передавал бы исключение в приложение, если оно было брошено в lib, а затем с помощью сообщений и файлов журнала ошибок для отчета и регистрации. Другой способ (в основном для служб) - использовать журнал событий Windows.
Основная проблема заключается в сборке сообщения об ошибке. Чтобы предоставить некоторую информацию об ошибке, я бы хотел задать статическое сообщение об ошибке (может быть строковым литералом, лучше содержать запись в файле сообщения, затем использовать FormatMessage) и включать некоторую информацию о времени выполнения, такую ​​как стек вызовов.
Функции/методы, необходимые для такого использования либоC++/Windows: Как сообщить об исключении из памяти (bad_alloc)?

  • СТЛ (std::string, std::stringstream, std::ofstream)
  • ЭЛТ (swprintf_s, fwrite)
  • или Win32 API (StackWalk64, MessageBox, FormatMessage, ReportEvent, WriteFile)

Помимо того, что документально на MSDN, все из них более (Win32) или менее (STL) закрыты в Windows, поэтому я не знаю, как они ведут себя при проблемах с низкой памятью.

Просто, чтобы доказать, что может быть проблемы, я написал тривиальное маленькое приложение провоцируя bad_alloc:

int main() 
{ 
    InitErrorReporter(); 

    try 
    { 
     for(int i = 0; i < 0xFFFFFFFF; i++) 
     { 
      for(int j = 0; j < 0xFFFFFFFF; j++) 
      { 
       char* p = new char; 
      } 
     } 
    }catch(bad_alloc& e_b) 
    { 
     ReportError(e_b); 
    } 

    DeinitErrorReporter(); 

    return 0; 
} 

Ран два экземпляра без отладчика прилагается (в конфигурации Release, VS 2008), но «ничего не произошло », то есть коды ошибок из ReportEvent или WriteFile, которые я использовал внутри отчета об ошибках. Затем запустили один экземпляр с одним отладочным отладчиком и попросили сообщить об ошибках один за другим, используя точку останова на линии ReportError. Это отлично работает для экземпляра с прикрепленным отладчиком (правильно зарегистрированный & зарегистрировал ошибку, даже используя проблемы LocalAlloc без проблем)! Но у Taskman было странное поведение, когда до выхода приложения много свободного места, я полагаю, когда вызывается исключение.


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

Заранее благодарю вас!

+2

Вы считаете, что зарезервированный заранее зарезервированный блок памяти станет источником размещения когда система попадает в ситуацию с нехваткой памяти? Я только когда-либо использовал этот метод ради изящества из приложения, но операционные системы (OpenSolaris, Linux) делают что-то похожее, чтобы дать приложению достаточно времени для бесплатно или заменить низкоприоритетные ассигнования и изящно восстановить – Sniggerfardimungus

+0

В настоящее время, Я использую некоторое пространство стека (переменные-члены, объявленные при вызове InitErrorReporter) для предоставления буферов для функций CRT/WinSDK. Но я не знаю, что они делают внутри, - см. Anser Alex Farber. – dyp

+0

http://stackoverflow.com/questions/1308052/policy-with-catching-stdbad-alloc talk about something like – Chubsdad

ответ

2
  • предварительно выделить буфер (ы) вам нужно
  • ссылка статически и использовать _beginthreadex вместо CreateThread (в противном случае, CRT функции могут не) - ИЛИ - реализовать строка CONCAT/I2A себе
  • Использование MessageBox (MB_SYSTEMMODAL | MB_OK) MSDN упоминает об этом для представления условий ОЫХ (а некоторые MS блоггер описал такое поведение как задумано: окно сообщения не будет выделять память.)

Logging сложнее, по крайней мере, файл журнала должен быть открыт уже.

Возможно, лучше всего с FILE_FLAG_NO_BUFFERING и FILE_FLAG_WRITE_THROUGH, чтобы избежать попыток буферизации. Первый требует, чтобы записи и буферы памяти были выровнены по сектору (т. Е. Вам нужно запросить GetDiskFreeSpace, выровняйте свой буфер и напишите только на смещения файлов «multiple of sector size» и в блоках, кратных размеру сектора. Я не уверен, что это необходимо или помогает, но общесистемный OOM, где с ошибкой распределения сложно моделировать.

+1

большое спасибо. Я сделал несколько тестов (но я не знаю, насколько они значимы) и MessageBox | MB_SYSTEMMODAL всегда появлялся, даже после обнаружения нескольких bad_allocs. Для этого я использовал некоторый код, основанный на одном в своем собственном ответе, но решил построить его для x64/Release. Bad_allocs, которые я получил с x86, были в основном ошибками вне виртуального адреса, так как у меня есть 4 ГБ ОЗУ плюс (сейчас) размер файла макс. 2 ГБ. С версией x64 система действительно сильно зависает, даже иногда курсор. Но все же, МБ работает и этот трюк со страницей, о которой я упоминал, тоже! – dyp

3

«Освобождение предварительно выделенного пространства кучи ...». Это было именно то, что я подумал прочесть ваш вопрос. Но я думаю, вы можете попробовать. Каждый процесс имеет собственное пространство виртуальной памяти. С другими процессами, требующими большой памяти, это все равно может работать, если весь компьютер работает.

+0

k, но предполагал там, где два потока в одном приложении, где оба выполняли функции разных библиотек. Один процесс -> одно виртуальное пространство памяти. Итак, когда кто-то бросает bad_alloc, то освобождает предварительно выделенное пространство, другое может выделить его? Проблема в том, что я использую функции OS/SDK, я не знаю, полагаются ли они на внутреннее пространство, поэтому использование предварительно выделенного пространства для работы не было решением, которое я думал. – dyp

+0

Я должен приостановить все остальные темы, прежде чем я смогу использовать эту технику? – dyp

+0

Другие потоки могут сразу использовать предварительно выделенную память, которая только что была выпущена ... Каждое действие должно быть остановлено, чтобы убедиться, что отчет об ошибке успешно завершен. –

1

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

В Windows (и других современных операционных системах) каждый процесс имеет собственное адресное пространство (aka memory), отдельное от каждого другого запущенного процесса. И все это отдельно от буквальной ОЗУ в машине. Операционная система виртуализировала адресное пространство процесса от физической памяти.

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

Это также то, как один процесс может выделять больше памяти, чем машина имеет физическую память и все еще работает. Например, программа, работающая на машине с 512 МБ ОЗУ, может по-прежнему выделять 1 ГБ памяти. Windows просто не могла бы сохранить все это в ОЗУ одновременно, а некоторые из них были бы в файле. Но программа не знала.

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

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

+0

Согласен. Что случилось с двумя потоками? См. Ответ Алекса Фарбера. – dyp

+0

А? Это неверно. Хотя каждый процесс имеет свое собственное пространство VA, ограниченное количество физических кадров и пространство подкачки удовлетворяют запросу на странице. Если общая сумма фиксации превышает этот номер, запросы на распределение каждого процесса терпят неудачу. Если вы столкнетесь с этой ситуацией, освобождение части памяти означает, что любой процесс в системе добирается до нее (т. Е. Любой процесс сможет снова повысить плату фиксации). –

+0

@Paul: У вас есть точка. Независимо от того, что делает ОС, в конечном итоге всегда будет предел. Несмотря на то, что вне ситуаций, когда механизмы памяти ОС были отключены, я задаюсь вопросом, не разумно ли для программ действовать так, как будто они всегда будут иметь полное виртуальное адресное пространство для работы. Но я предполагаю, что это может быть зависящим от конкретной ситуации, в зависимости от того, насколько вероятно, что конечная машина будет максимально использовать ее ресурсы и насколько полезной будет логика в ответ на такую ​​ситуацию. – TheUndeadFish

0

Вы не можете использовать функции CRT или MessageBox для обработки OOM, поскольку они могут потребоваться для хранения памяти, как вы описали. Единственная по-настоящему безопасная вещь, которую вы можете сделать, это выделить кусок памяти при запуске, вы можете записать информацию и открыть дескриптор файла или трубы, а затем WriteFile, когда вы выйдете из OOM.

+0

Итак, вы имеете в виду, вместо того, чтобы предварительно выделять, а затем освобождать кусок, вместо этого вы предварительно выделите область, которая будет использоваться специально для обработки данных, записанных в сценарии OOM (своего рода буфер для буфера для сборки строки или что угодно). Предполагая, что WriteFile никогда не будет иметь причины сбой в сценарии OOM, тогда я должен согласиться с тем, что это звучит как лучший гарантированный способ записи информации. – TheUndeadFish

+0

Да, это то, что делает ОС, если оно должно гарантировать, что что-то работает в условиях отсутствия памяти (например, возможность повысить исключение SEH, ntdll всегда сохраняет один предварительно выделенный в заднем кармане) –

+0

Знаете ли вы, создание различных видов объектов Win32 ведет себя в условиях OOM? То есть Объекты ядра, такие как файлы (-> дескрипторы файлов) и объекты GUI, такие как MessageBoxes? Насколько я знаю, объекты Kernel, по крайней мере, не являются частью вашего пространства виртуальной памяти/адреса, и поэтому ОС фактически выделяет для них память. – dyp

Смежные вопросы