Оптимистичный malloc для Linux: новое всегда будет выбрасывать, когда не хватает памяти?

Я читал об условиях нехватки памяти в Linux, и следующий абзац из страниц руководства заставил меня задуматься:

По умолчанию Linux следует оптимистичной стратегии выделения памяти. Это означает, что когда malloc() возвращает не NULL, нет никакой гарантии, что память действительно доступна. Это действительно плохая ошибка. Если окажется, что системе не хватает памяти, один или несколько процессов будут уничтожены печально известным убийцей OOM. [...]

Учитывая, что новая реализация оператора в конечном итоге вызовет malloc, есть ли какие-либо гарантии, что new на самом деле вызовет Linux? Если нет, то как справиться с этой явно необнаружимой ошибочной ситуацией?

5 ответов

Решение

Это зависит; Вы можете настроить параметры overcommit ядра, используя vm.overcommit_memory.

Херб Саттер несколько лет назад обсуждал, как это поведение на самом деле не соответствует стандарту C++:

"В некоторых операционных системах, включая, в частности, Linux, выделение памяти всегда происходит успешно. Полная остановка. Как выделение может всегда выполняться успешно, даже если запрошенная память действительно недоступна? Причина в том, что само выделение просто записывает запрос на память; под прикрытием (физическая или виртуальная) память фактически не выделяется запрашивающему процессу с реальным резервным хранилищем до тех пор, пока память не будет фактически использована.

"Обратите внимание, что если new использует средства операционной системы напрямую, то new всегда будет успешным, но любой последующий невинный код, такой как buf[100] = 'c'; может бросить, завершить с ошибкой или остановить. С точки зрения стандартного C++ оба эффекта являются несоответствующими, потому что стандарт C++ требует, чтобы, если new не мог зафиксировать достаточно памяти, он должен был потерпеть неудачу (это не так), и такой код, как buf [100] = 'c', не должен выдавать исключение или иначе вызывать ошибку (это может быть)."

Вы не можете справиться с этим в своем программном обеспечении, чисто и просто.

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

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

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

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

Я думаю, что malloc все еще может вернуть NULL. Причина в том, что существует разница между доступной системной памятью (RAM + swap) и объемом в адресном пространстве вашего процесса.

Например, если вы запрашиваете 3 ГБ памяти у malloc на стандартном Linux x86, он обязательно вернет NULL, поскольку это невозможно, учитывая объем памяти, предоставленный приложениям пользовательского пространства.

Да, есть одна гарантия, что новый в итоге выкинет. Независимо от чрезмерной загрузки, объем адресного пространства ограничен. Поэтому, если вы продолжите выделять память, рано или поздно у вас кончится адресное пространство, и new будет вынужден выбросить.

Другие вопросы по тегам