Классическое управление памятью Mac OS - Classic Mac OS memory management

Окно «Об этом компьютере» Mac OS 9.1, показывающее потребление памяти каждым открытым приложением и самим системным программным обеспечением.

Исторически сложилось так, что классическая Mac OS использовала форму управления памятью , которая потеряла популярность в современных системах. Критика такого подхода была одна из ключевых областей , рассматриваемых на смену Mac OS X .

Первоначальная проблема для инженеров Macintosh заключалась в том, как оптимально использовать 128 КБ ОЗУ, которым была оснащена машина, на компьютерном оборудовании на базе Motorola 68000, которое не поддерживает виртуальную память . Поскольку в то время машина могла запускать только одну прикладную программу за раз и не было фиксированного вторичного хранилища , инженеры реализовали простую схему, которая хорошо работала с этими конкретными ограничениями. Этот выбор конструкции не очень хорошо масштабировался при разработке машины, создавая различные трудности как для программистов, так и для пользователей.

Фрагментация

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

Система управления памятью имела слабые места; системная куча не была защищена от ошибочных приложений, что было бы возможно, если бы архитектура системы поддерживала защиту памяти , и это часто было причиной системных проблем и сбоев. Кроме того, подход на основе дескрипторов также открыл источник ошибок программирования, когда нельзя было гарантировать, что указатели на данные в таких перемещаемых блоках останутся действительными при вызовах, которые могут вызвать перемещение памяти. Это было настоящей проблемой почти для всех существующих системных API . Из-за прозрачности структур данных, принадлежащих системе в то время, API-интерфейсы мало что могли сделать для решения этой проблемы. Таким образом, обязанность программиста - не создавать такие указатели или, по крайней мере, управлять ими очень осторожно, разыменуя все дескрипторы после каждого такого вызова API. Поскольку многие программисты не были знакомы с этим подходом, ранние программы для Mac часто страдали от связанных с этим ошибок.

Palm OS и 16-битная Windows используют аналогичную схему управления памятью, но версии для Palm и Windows усложняют ошибку программиста. Например, в Mac OS для преобразования дескриптора в указатель программа просто отменяет ссылку на дескриптор напрямую, но если дескриптор не заблокирован, указатель может быстро стать недействительным. Вызовы блокировки и разблокировки дескрипторов не сбалансированы; десять вызовов в HLock адрес отменяются одним вызовом HUnlock . В Palm OS и Windows дескрипторы являются непрозрачным типом, и MemHandleLock в Palm OS или Global/LocalLock Windows необходимо отменить ссылку на них. Когда приложение Palm или Windows завершает работу с дескриптором, оно вызывает MemHandleUnlock или Global/LocalUnlock . Palm OS и Windows ведут счет блокировок для блоков; после трех обращений к MemHandleLock , блок будет разблокирован только после трех обращений к MemHandleUnlock .

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

Утечки памяти и устаревшие ссылки

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

Коммутатор

Ситуация ухудшилась с появлением Switcher , который позволил Mac с объемом памяти 512 КБ и более запускать сразу несколько приложений. Это был необходимый шаг вперед для пользователей, которые сочли подход «одно приложение за раз» очень ограничивающим. Поскольку Apple теперь была привержена своей модели управления памятью, а также совместимости с существующими приложениями, она была вынуждена принять схему, в которой каждому приложению выделялась собственная куча из доступной оперативной памяти. Фактический объем оперативной памяти, выделенной для каждой кучи, задавался значением, закодированным в метаданных каждого приложения, установленным программистом. Иногда этого значения было недостаточно для определенных видов работы, поэтому параметр значения должен был быть предоставлен пользователю, чтобы он мог настроить размер кучи в соответствии со своими требованиями. Хотя это было популярно среди « опытных пользователей », такое раскрытие деталей технической реализации противоречило сути философии пользователя Mac. Помимо того, что пользователи сталкивались с эзотерическими техническими особенностями, это было неэффективно, поскольку приложение заставляло захватить всю выделенную ему оперативную память, даже если впоследствии большая ее часть оставалась неиспользованной. Другое приложение может испытывать нехватку памяти, но не сможет использовать свободную память, «принадлежащую» другому приложению.

Хотя приложение не может эффективно использовать кучу сестринского приложения, оно определенно может уничтожить ее, как правило, путем непреднамеренной записи на бессмысленный адрес. Приложение, случайно обрабатывающее фрагмент текста или изображения или неназначенное местоположение как указатель, может легко перезаписать код или данные других приложений или даже ОС, оставив «скрытых» даже после выхода из программы. Такие проблемы может быть чрезвычайно сложно проанализировать и исправить.

Switcher превратился в MultiFinder в System 4.2, который стал Process Manager в System 7 , и к тому времени эта схема уже давно укоренилась. Apple предприняла некоторые попытки обойти очевидные ограничения - временная память была той, где приложение могло «занимать» свободную оперативную память, которая лежала вне его кучи, на короткие периоды времени, но это было непопулярно среди программистов, поэтому в значительной степени не решало проблемы. Дополнение Apple System 7 Tune-up добавило «минимальный» размер памяти и «предпочтительный» размер - если предпочтительный объем памяти был недоступен, программа могла запускаться в минимальном пространстве, возможно, с ограниченной функциональностью. Это было включено в стандартную ОС, начиная с System 7.1, но все еще не решало основную проблему.

Схемы виртуальной памяти , которые делали доступной больше памяти за счет подкачки неиспользуемых частей памяти на диск, стали доступны сторонними утилитами, такими как Connectix Virtual , а затем Apple в System 7 . Это увеличило объем памяти Macintosh за счет снижения производительности, но не добавило защищенной памяти и не предотвратило сжатие кучи диспетчера памяти, которое сделало бы недействительными некоторые указатели.

32-битный чистый

Первоначально у Macintosh было 128 КБ ОЗУ с ограничением 512 КБ. После выпуска Macintosh Plus он был увеличен до 4 МБ . Эти компьютеры Macintosh использовали процессор 68000 , 32-разрядный процессор, но имели только 24 физических адресных строки. 24 строки позволяли процессору адресовать до 16 МБ памяти (2 24 байта), что в то время считалось достаточным объемом. Предел ОЗУ в дизайне Macintosh составлял 4 МБ ОЗУ и 4 МБ ПЗУ из-за структуры карты памяти. Это было исправлено путем изменения карты памяти в Macintosh II и Macintosh Portable , что позволило использовать до 8 МБ ОЗУ.

Поскольку память была ограниченным ресурсом, авторы Mac OS решили воспользоваться неиспользуемым байтом в каждом адресе. Исходный диспетчер памяти (до появления Системы 7) помещал флаги в старшие 8 бит каждого 32-битного указателя и дескриптора . Каждый адрес содержал такие флаги, как «заблокирован», «очищаемый» или «ресурс», которые хранились в таблице главных указателей. При использовании в качестве фактического адреса эти флаги маскировались и игнорировались ЦП.

Несмотря на хорошее использование очень ограниченного пространства ОЗУ, такая конструкция вызвала проблемы, когда Apple представила Macintosh II, в котором использовался 32-разрядный процессор Motorola 68020 . 68020 имел 32 физических адресных строки, которые могли адресовать до 4 ГБ (2 32 байта) памяти. Флаги, которые диспетчер памяти хранил в старшем байте каждого указателя и дескриптора, теперь имеют большое значение и могут привести к ошибкам адресации.

Теоретически архитекторы системного программного обеспечения Macintosh могли свободно изменять схему «флаги в старшем байте», чтобы избежать этой проблемы, и они это сделали. Например, на компьютерах Macintosh IIci и более поздних версий и HLock() другие API-интерфейсы были переписаны, чтобы реализовать блокировку дескрипторов другим способом, кроме отметки старших битов дескрипторов. Но многие программисты приложений Macintosh и значительная часть самого программного кода системы Macintosh обращались к флагам напрямую, а не с помощью API-интерфейсов, таких как HLock() , которые были предоставлены для управления ими. Сделав это, они сделали свои приложения несовместимыми с истинной 32-битной адресацией, и это стало известно как «не 32-битная чистая».

Чтобы остановить постоянные сбои системы, вызванные этой проблемой, Система 6 и более ранние версии, работающие на 68020 или 68030, переводили машину в 24-битный режим и распознавали и исправляли только первые 8 мегабайт ОЗУ, что является очевидным недостатком в машины, оборудование которых было подключено к оперативной памяти до 128 МБ, и чья литература о продуктах рекламировала эту возможность. В System 7 системное программное обеспечение Mac, наконец, стало 32-битным, но проблема с грязными ПЗУ все еще оставалась. Проблема заключалась в том, что решение использовать 24-битную или 32-битную адресацию нужно было принять очень рано в процессе загрузки, когда подпрограммы ПЗУ инициализировали диспетчер памяти для настройки базовой среды Mac, в которую загружаются ПЗУ NuBus и драйверы дисков. и казнен. В старых ПЗУ не было поддержки 32-разрядного диспетчера памяти, поэтому невозможно было загрузиться в 32-разрядном режиме. Удивительно, но первое решение этой проблемы было опубликовано компанией Connectix , производящей программные утилиты , чей продукт MODE32 1991 года повторно инициализировал диспетчер памяти и повторил ранние части процесса загрузки Mac, позволяя системе загружаться в 32-разрядном режиме и позволяя использовать все RAM в машине. Apple лицензировала программное обеспечение Connectix позже в 1991 году и распространяла его бесплатно. Macintosh IIci , а затем Motorola компьютеры Macintosh на основе имели 32-разрядные чистые диски.

Прошло довольно много времени, прежде чем приложения были обновлены для удаления всех 24-битных зависимостей, и Система 7 предоставила способ вернуться в 24-битный режим, если обнаружены несовместимости приложений. Ко времени перехода на PowerPC и System 7.1.2 32-битная чистота была обязательной для создания собственных приложений, и даже более поздние версии Mac на базе Motorola 68040 не могли поддерживать 24-битный режим.

Ориентация объекта

Появление объектно-ориентированных языков программирования для Mac - сначала Object Pascal , а затем C ++ - также вызвало проблемы для принятой модели памяти. Сначала казалось бы естественным, что объекты будут реализованы с помощью дескрипторов, чтобы получить преимущество возможности перемещения. Эти языки, в том виде, в котором они были изначально разработаны, использовали указатели на объекты, что приводило к проблемам фрагментации. Решением, реализованным компиляторами THINK (позже Symantec ) , было внутреннее использование дескрипторов для объектов, но использование синтаксиса указателя для доступа к ним. Сначала это казалось хорошей идеей, но вскоре возникли серьезные проблемы, поскольку программисты не могли сказать, имеют ли они дело с перемещаемым или фиксированным блоком, и поэтому не имели возможности узнать, брать ли на себя задачу блокировки объектов или нет. Излишне говорить, что это привело к огромному количеству ошибок и проблем с этими ранними реализациями объектов. Более поздние компиляторы не пытались это сделать, но использовали настоящие указатели, часто реализуя собственные схемы распределения памяти для обхода модели памяти Mac OS.

В то время как модель памяти Mac OS со всеми присущими ей проблемами оставалась такой вплоть до Mac OS 9 , из-за серьезных ограничений совместимости приложений растущая доступность дешевой оперативной памяти означала, что в целом большинство пользователей могли обновить свой выход из строя. угол. Память использовалась неэффективно, но ее было достаточно, чтобы проблема никогда не становилась критической. Это иронично, учитывая, что цель первоначального дизайна заключалась в максимальном использовании очень ограниченного объема памяти. Mac OS X, наконец, покончила со всей схемой, реализовав современную схему разреженной виртуальной памяти . Подмножество API-интерфейсов старой модели памяти все еще существует для совместимости как часть Carbon , но сопоставляется с современным менеджером памяти (поточно-ориентированной malloc реализацией) внизу. Apple , рекомендует Mac OS X использование кода malloc и free «почти исключительно».

Рекомендации

внешняя ссылка