Как многопоточное приложение C должно обработать неудавшийся malloc ()?

Часть приложения, я продолжаю работать, является простым находящимся в pthread сервером, который связывается по сокету TCP/IP. Я пишу это в C, потому что это будет выполнением в ограниченной среде памяти. Мой вопрос: что должна сделать программа, если один из потоков встречается с malloc (), который возвращает ПУСТОЙ УКАЗАТЕЛЬ? Возможности я придумал до сих пор:

  1. Никакая специальная обработка. Позвольте malloc (), возвращают ПУСТОЙ УКАЗАТЕЛЬ и позволяют ему быть разыменованным так, чтобы все это segfaults.
  2. Выйдите сразу на неудавшемся malloc () путем вызова аварийного прекращения работы () или выход (-1). Предположите, что среда очистит все.
  3. Выпрыгните из основного цикла событий и делайте попытку к pthread_join () всех потоков, затем закройтесь.

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

Что я сделаю?

9
задан ipartola 14 May 2010 в 11:28
поделиться

6 ответов

Это одна из причин, по которой жесткие системы space / rad обычно запрещают динамическое выделение памяти. Когда malloc () дает сбой, очень сложно «вылечить» сбой. У вас есть несколько вариантов:

  • Вам не обязательно использовать встроенную библиотеку libc malloc () (вообще или как обычно). Вы можете обернуть malloc () , чтобы выполнять дополнительную работу при сбоях, например, уведомлять что-то еще. Это полезно при использовании чего-то вроде сторожевого пса. Вы также можете использовать полноценный сборщик мусора , хотя я не рекомендую его. Лучше выявить и устранить утечки.
  • В зависимости от хранилища и сложности, редко используемые выделенные блоки могут отображаться на диск. Но здесь, как правило, вы экономите всего несколько килобайт физической памяти.
  • Вы можете использовать статический пул памяти и свой собственный malloc () , который не будет переоценивать его. Если вы тщательно профилировали использование кучи (с помощью такого инструмента, как Valgrind massif или аналогичного), вы можете разумно изменить размер пула.

Однако большинство из этих предложений сводятся к тому, чтобы не доверять / использовать систему malloc () , если отказ не возможен.

В вашем случае, я думаю, лучшее, что вы можете сделать, это сделать убедиться, что сторожевой таймер будет уведомлен в случае сбоя malloc () , чтобы ваш процесс (или всю систему) можно перезапустить. Вы же не хотите, чтобы он выглядел «живым и работающим» в тупике.Это может быть так же просто, как просто отменить связь с файлом.

Вести очень подробные журналы. В каком файле / строке / функции произошел сбой?

Если malloc () не удается получить всего несколько КБ, это хороший признак того, что ваш процесс действительно не может продолжаться надежно в любом случае.Если ему не удастся захватить несколько сотен МБ, вы сможете восстановиться и продолжить работу. По этому признаку любое действие, которое вы предпринимаете, должно основываться на том, сколько памяти вы пытаетесь получить, и если вызовы для выделения гораздо меньшего размера по-прежнему будут успешными.

Единственное, чего вы никогда не должны делать, - это просто работать с NULL-указателями и дать им потерпеть крах. Это просто небрежно, не дает полезного журнала, где что-то пошло не так, и создает впечатление, что ваше программное обеспечение имеет низкое / нестабильное качество.

4
ответ дан 4 December 2019 в 21:08
поделиться

Это работает в ОС? Об этом свидетельствует использование pthreads. Вы знаете, что malloc () когда-либо вернет NULL? В некоторых системах (например, Linux) ошибка возникает в malloc () и обрабатывается ОС (путем остановки процесса) без возврата malloc ().

Я бы посоветовал вам выделить пул памяти при инициализации вашего приложения и выделить из него, а не использовать malloc () после инициализации. Это даст вам контроль над алгоритмом распределения памяти и поведением при исчерпании памяти. Если для пула недостаточно памяти, при инициализации будет единая точка отказа, прежде чем ваше приложение сможет запустить что-то, что не может завершить.

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

0
ответ дан 4 December 2019 в 21:08
поделиться

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

Это немного усложняет очистку, а также затрудняет выработку общего решения.

0
ответ дан 4 December 2019 в 21:08
поделиться

Есть четвертый вариант: освободить немного памяти (кеши всегда хорошие кандидаты) и повторить попытку.

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

2
ответ дан 4 December 2019 в 21:08
поделиться

Зависит от вашей архитектуры, я думаю.

Означает ли ошибка malloc(), что только этот поток не может продолжить работу, или же весь процесс нарушается в этом случае?

Обычно, когда память действительно ограничена (т.е. в микропроцессорных средах), хорошей идеей является избегать ВСЕХ динамических распределений памяти, чтобы избежать проблем, подобных этой.

0
ответ дан 4 December 2019 в 21:08
поделиться

В варианте 2 нет ничего плохого. Вы не должны предполагать - exit () завершает процесс, что означает, что все нити оборваны, и все убрано.

Не забудьте попытаться записать, где произошло сбойное выделение.

4
ответ дан 4 December 2019 в 21:08
поделиться
Другие вопросы по тегам:

Похожие вопросы: