Как многопоточное приложение C должно обрабатывать сбой malloc ()? - PullRequest
9 голосов
/ 14 мая 2010

Часть приложения, над которым я работаю, - это простой сервер на основе pthread, который обменивается данными через сокет TCP / IP. Я пишу это на C, потому что он будет работать в среде с ограниченным объемом памяти. Мой вопрос: что должна делать программа, если один из потоков встречает malloc (), который возвращает NULL? Возможности, которые я придумала до сих пор:

  1. Нет специальной обработки. Пусть malloc () возвращает NULL и разыменовывается, так что все происходит с ошибками.
  2. Немедленно завершите работу с ошибкой malloc (), вызвав abort () или exit (-1). Предположим, что среда все очистит.
  3. Выпрыгните из цикла основного события и попытайтесь pthread_join () всех потоков, затем выключите.

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

Что мне делать?

Ответы [ 6 ]

4 голосов
/ 14 мая 2010

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

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

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

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

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

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

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

4 голосов
/ 14 мая 2010

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

Не забудьте попробовать и записать, где произошло неудачное размещение.

2 голосов
/ 14 мая 2010

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

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

0 голосов
/ 14 мая 2010

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

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

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

0 голосов
/ 14 мая 2010

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

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

0 голосов
/ 14 мая 2010

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

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

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

...