Как оригинальное ядро Unix управляло памятью?

Вопрос или проблема

Сегодня практически все ядра используют виртуальную память, предоставляемую MMU. Они делают это с помощью глобальной таблицы страниц, адрес которой находится в регистре ЦП, и супервизора/маппера страниц для процессов. “vm” в vmlinuz, например, означает, что ядро Linux поддерживает виртуальную память.

Все это возможно, потому что MMU мапирует непрерывные адреса памяти на сегменты памяти, понятные архитектуре x86.

Оригинальное ядро UNIX действительно имело версию vmunix, которое, как я полагаю, должно было использовать аналогичную технику. Тем не менее, оригинальное ядро UNIX было написано до появления MMU. Если я не ошибаюсь, оригинальное ядро UNIX (которое просто называлось unix) было написано до существования архитектуры x86. Исторически оно работало на PDP-9 и PDP-11.

Как это ядро выполняло адресацию и управление памятью? Использовалось ли сегментное адресование (две цифры) или полное адресование памяти (одна цифра)? Как оно разделяло память между процессами?

Виртуальная память почти на десятилетие старше Unix: она появилась в Burroughs B5000 в 1961 году. У нее не было MMU в современном понимании (т.е. основанной на страницах), но она обеспечивала те же основные функции. IBM System/360 Model 67 в 1965 году (все еще старше Unix) имела MMU. Процессоры Intel x86 получили MMU только с 80286 в 1982 году.

Реализация системы Unix на самом деле не требует MMU. Она требует какой-то формы виртуальной памяти, иначе реализация fork системного вызова оказывается чрезмерно сложной. Системный вызов fork, для создания процессов путем копирования существующего процесса, был основополагающей частью Unix с самой первой версии, поэтому он действительно требовал виртуальную память. См. D. M. Ritchie и K. Thompson, The UNIX Time-Sharing System, CACM, 1974, §V “Процессы и изображения”.

Я не знаю деталей аппаратного обеспечения, на котором работали первые версии Unix, но у них действительно была виртуальная память в виде сегментированной архитектуры. ЦП преобразовывал указатели, разыменованные программой (виртуальные адреса), в фактические местоположения в памяти (физические адреса). Сопоставление выполнялось добавлением смещения к виртуальному адресу. При каждом переключении контекста между процессами регистр, содержащий смещение, корректировался.

Хотя практически все реализации Unix обеспечивают изоляцию процессов, это не касалось некоторых исторических реализаций на аппаратном обеспечении без защиты памяти (как в 1970-х, так и в 1980-х с MINIX на 8088 и 80286). Защита памяти несколько ортогональна виртуализации адресов; MMU предоставляет и то, и другое, простая сегментированная архитектура этого не делает, MPU¹ обеспечивает защиту без виртуализации. Существует реализация Linux для систем без MMU, uCLinux, но из-за отсутствия fork многие программы не могут работать (единственным поддерживаемым fork является vfork, что требует вызова execve в дочернем процессе сразу после этого).

¹ MPU (блок защиты памяти) регистрирует права доступа для каждой страницы памяти.

Вам не нужен постраничный MMU для получения защиты памяти.

Постраничные MMU решают много более сложных задач, таких как фрагментация памяти (недоступность больших блоков) и mmap (сохранение только недавно использованных частей файла в памяти). И постраничные MMU необходимы для реализации “унифицированного кэша страниц” для кэширования доступа к файлам для всех процессов.

Но базовую защиту памяти можно реализовать только с помощью более простой “сегментации”, которая, вероятно, была доступна ранним реализациям Unix. Например, защищенная память ядра может быть помещена в сегмент, который устанавливается в “нет доступа”, когда выполняется код в пользовательском режиме. Даже в современном управляемом операционном окружении Linux системные вызовы brk() и sbrk() существуют как оставшиеся для совместимости с сегментированными реализациями.

В сущности, сегментация похожа на постраничный MMU, за исключением того, что вы получаете только фиксированное количество страниц переменного размера (называемых сегментами), вместо переменного числа страниц фиксированного размера.

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

Ответ или решение

Как оригинальная версия ядра Unix осуществляла адресацию памяти

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

Исторический контекст

Организация памяти в первоначальной версии UNIX была адаптирована для архитектур, на которых она работала, таких как PDP-9 и PDP-11. Последние имели свои собственные механизмы адресации, не прибегая к технологиям, доступным современным архитектурам, таким как x86 или ARM. Это означало, что для реализации базовых функций, таких как системный вызов fork, требовалась какая-то форма виртуальной памяти.

Адресация памяти

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

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

Проблемы защиты памяти

На платформах того времени, такие как PDP-11, не существовало полноценной аппаратной защиты памяти. Это означало, что все процессы работали в одном адресном пространстве и могли потенциально взаимодействовать друг с другом. Однако реализация базовой функциональности управления памятью все же позволяла некоторую изоляцию между процессами, если каждая из них работала в ограниченном сегменте, имеющем определенные права доступа.

Итого

Таким образом, оригинальное ядро Unix осуществляло адресацию и управление памятью через сегментированную архитектуру. Хотя оно не имело полного контроля памяти, характерного для MMU, механизмы сегментации позволяли программам использовать разделение памяти и как следствие относительную изоляцию процессов.

Эти подходы задали основу для дальнейшего развития систем управления памятью, которые привели к современным методам виртуализации, внедренным в предыдущие версии Unix и её современных производных, таких как Linux.

Вывод

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

Оцените материал
Добавить комментарий

Капча загружается...