Operating system - файл - Где несколько стеков и кучей, помещенных в виртуальную память?

operating system - файл - Где несколько стеков и кучей, помещенных в виртуальную память?

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

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

Управление памятью является дорогостоящим, и поэтому он захватывает ядро. Объединение двух может нанести серьезный удар производительности, поэтому то, что кажется фактическим управлением кучей с точки зрения приложения, фактически реализовано в пользовательском пространстве (библиотека времени выполнения C) ради производительности (и по другой причине из области действия на данный момент ).

При загрузке библиотеки общих библиотек (DLL), если она загружается при запуске программы, она, конечно, будет, скорее всего, загружена в CODE / DATA / etc, поэтому не произойдет фрагментация кучи. С другой стороны, если он загружен во время выполнения, у него не будет другого шанса, кроме использования кучи. Статические библиотеки, конечно же, просто связаны с разделами CODE / DATA / BSS / etc.

В конце дня вам нужно будет наложить ограничения на кучи и стеки, чтобы они не переполнялись, но вы можете выделить других. Если нужно превысить этот предел, вы можете

  • Завершить приложение с ошибкой
  • Пусть менеджер памяти выделяет / изменяет размер / перемещает блок памяти для этого стека / кучи и, скорее всего, дефрагментирует кучу (ее собственный уровень); поэтому free() обычно работает плохо.

Учитывая довольно большой стек стека 1 КБ при каждом call в среднем (может произойти, если разработчик приложения не изучен), 10 МБ-стек будет достаточным для 10240 вложенных call -s. Кстати, кроме того, в значительной степени нет необходимости в более чем одном стеке и куче на поток.

Я пишу ядро ​​и нуждаюсь (и хочу) ставить несколько стеков и кучей в виртуальную память, но я не могу понять, как их эффективно разместить. Как это делают обычные программы?

Как (или где) находятся стеки и кучи, помещенные в ограниченную виртуальную память, предоставляемую 32-битной системой, так что они имеют как можно больше пространства для роста?

Например, когда тривиальная программа загружается в память, макет его адресного пространства может выглядеть следующим образом:

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

Многие программы имеют общие библиотеки, которые помещаются где-то в виртуальное адресное пространство. Затем есть многопоточные программы, которые имеют несколько стеков, по одному для каждого потока. И .NET-программы имеют многократные кучи , все из которых должны иметь возможность расти так или иначе.

Я просто не вижу, как это делается достаточно эффективно, не ставя предопределенного ограничения на размер всех куч и стеков.

Я предполагаю, что у вас есть основы в вашем ядре, обработчик ловушек для ошибок страниц, которые могут отображать страницу виртуальной памяти в ОЗУ. На следующем уровне вам понадобится диспетчер адресного пространства виртуальной памяти, из которого код usermode может запрашивать адресное пространство. Выберите гранулярность сегмента, которая предотвращает чрезмерную фрагментацию, 64 КБ (16 страниц) является хорошим числом. Разрешить использовать код для резервирования и зарезервировать пространство. Простая растровая карта 4GB / 64KB = 64K x 2 бит для отслеживания состояния сегмента выполняет задание. Обработчик ошибок с ошибкой страницы также должен проконсультироваться с этим растровым изображением, чтобы узнать, действителен ли запрос страницы.

Стек представляет собой выделение VM с фиксированным размером, обычно 1 мегабайт. Для потока обычно требуется только несколько страниц, в зависимости от уровня вложенности функций, поэтому резервируйте 1 МБ и перенесите только верхние несколько страниц. Когда поток гнездится глубже, он отключит ошибку страницы, и ядро ​​может просто отобразить дополнительную страницу в ОЗУ, чтобы поток продолжался. Вы захотите пометить нижние несколько страниц как специальные, когда страница нитей называет ошибки, вы объявляете имя этого сайта.

Важнейшей задачей менеджера кучи является предотвращение фрагментации. Лучший способ сделать это - создать список lookaside, который разделяет запросы кучи по размеру. Все, что меньше 8 байт, поступает из первого списка сегментов. От 8 до 16 от второго, от 16 до 32 от третьего и т. Д. Увеличение размера ведра по мере продвижения. Вам нужно будет играть с размерами ковша, чтобы получить лучший баланс. Очень большие ассигнования поступают непосредственно из диспетчера адресов VM.

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

📎📎📎📎📎📎📎📎📎📎