Сергей Холодилов ([info]fat_crocodile) wrote,
@ 2009-03-05 10:51:00
Previous Entry  Add to memories!  Tell a Friend  Next Entry
Entry tags:программинг

Виртуальная память - 0
Скорее всего, все знают, как устроена виртуальная память в Intel x86 ...

Не. Это вряд ли.

Скорее всего, многие видели картинки, более-менее изображающие устройство виртуальной памяти в Intel x86. Как выясняется, есть и другие забавные и поучительные варианты того же самого.

Это должен был быть один большой пост. Но он получается слишком большой и я его уже пишу слишком долго. Поэтому пусть будет много маленьких.

[Ликбез] Виртуальная память: что это и зачем

Вдруг кто-то не знал или забыл.

Итак: процессор, память, многозадачная операционная система, много задач (ака процессов). У каждого процесса есть какой-то исполняемый код, какие-то данные, чтобы всё это работало оно должно находиться в памяти. И тут возникает несколько проблем:
  • Процессы не должны мешать друг другу. В частности один процесс не должен иметь возможность случайно поменять данные/код другого процесса, иначе из-за ошибок в одном процессе будут падать все остальные (если такая возможность нужна, пусть попросит явно, это отдельный разговор).

  • Памяти мало. Всем сразу не хватает.

  • Памяти мало. А она ещё и фрагментируется. Допустим, загрузили процесс в память, за ним загрузили следующий, потом первый выгрузили. Осталась дырка. Эту память можно использовать только если будет загружаться процесс, по размеру не превышающий первый.

  • Вообще, "размер процесса" понятие очень условное. Память выделяется из кучи, памяти нужно то мало, то много, дать процессу один кусок навсегда -- очень не гибко

  • Памяти мало. Часто разные процессы используют один и тот же код. Разделяемые библиотеки, или просто одна и та же программа выполняется. Хорошо бы что бы при этом в памяти была только одна копия кода.

Для всего этого придумали сегментную виртуальную память, но она не удобным образом решала не все проблемы. Поэтому ещё немного подумали и придумали страничную виртуальную память. Это было настолько давно, что даже старики уже помнят только последнюю. И когда говорят "виртуальная память" имеется ввиду именно она. Но я начну свой рассказ с начала.

Сегментная виртуальная память

Сегментная виртуальная память требует сегментной адресации.

Простите, но что это такое я описывать не буду. Длинно, нужно рисовать картинки, не очень важно, да и по ходу разберётесь.

Т.е. процесс использует свои сегменты и в чужие не лезет. Это не сложно, это проверяется на уровне сегментных регистров: при попытке использовать сегмент, который нельзя, ОС получает тревожный сигнал (ака исключение), а процесс получает по башке (например, segmentation fault). Этим отличается защищённый режим от реального.

Термины реальный и защищённый режим вообще-то относятся к x86. В данном случае я применяю их нестрого, отношу к "защищённому режиму" те возможности, которые необходимы для реализации многозадачной ОС и которых не было в реальном режиме x86.

Это решение первой проблемы, но это ещё не виртуальная память.

Тут важно отметить, что процесс адресует память относительно начала сегмента, а где это начало он, по хорошему, понятия не имеет. А если имеет, то это ошибка проектирования ОС: не должен. Это ещё одно отличие защищённого режима от реального. А виртуальная память начинается с того, что ОС может временно выгрузить какой-то сегмент процесса из памяти, а когда этот сегмент опять понадобится процессу -- снова загрузить, возможно -- в другое место физической памяти (это термин; да, вот такое странное представление о физике).

Ну, допустим, как-то так:
  • Есть редко работающий процесс. Например, он отзывается на какие-то внешние события, типа прихода данных из сети.

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

  • И вот доходит дело до этого процесса. Он пытается обратиться к своему сегменту данных... Но тот помечен как выгруженный. ОС получает тревожный сигнал, загружает сегмент в память (возможно, выгрузив для этого что-то не нужное), помечает сегмент как нормальный, прописывает ему новый базовый адрес и просит процесс повторить попытку. Как в жизни.

Кто понимает, почему x86 не может так работать -- респект и уважуха :) Ну да, там всё будет немного сложнее, но это уже будет глубокий оффтопик.

Таким образом, выгружая и загружая, можно выполнять больше процессов, чем у нас памяти. Более того, разные процессы могут ссылаться на один и тот же сегмент кода, значит можно и сэкономить. Сложно с:
  • динамическим выделением памяти (в принципе решаемо, но тяжело)

  • фрагментацией физической памяти (хотя сегменты и можно перемещать, это довольно затратно)

Но главное -- это очень грубый инструмент, с низкой разрешающей способностью. Можно выгрузить/загрузить только целый сегмент, это много.

Страничная виртуальная память

Несложно заметить, что сегментная виртуальная память работает только за счёт того, что процесс адресует физическую память не напрямую, а немного косвенно. И, влезая в промежуточный уровень, ОС может незаметно для процесса химичить с памятью. Увеличиваем степень косвенности -- увеличиваем химические возможности ОС. По этому пути и пошли.

Итак, процессу предоставляется виртуальное адресное пространство (ВАП), каждому своё. Оно большое. По нонешним прогрессивным временам адресуемая область от 0 до 264-1, недавно стандартом было от 0 до 232-1. Это много, особенно если учесть что процессов не один, а физической памяти, скажем, 256 Мб, т.е. даже на одного не хватает.

Но оно на то и большое, чтобы быть с запасом. Считается, что большая его часть никогда не понадобится большей части процессов. Т.е. в этом огромном ВАП есть небольшие островки используемой памяти. Процесс может запросить себе дополнительный островок, или отпустить какой-то из используемых... Речь не про ограничения, расчёт именно на то, что их на самом деле не нужно слишком много.

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

Соответствие между используемыми островками ВАП и физической памятью происходит постранично. Т.е. ОС знает, что такой-то странице из ВАП такого-то процесса соответствует такая-то страница физической памяти. Есть какая-то таблица или что-то в этом роде. Неиспользуемым частям ВАП не соответствует ничего.

Это и есть дополнительный уровень косвенности.

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

  • меньше дискретность

  • В пределах ВАП элементарно регулируется объём используемой памяти, с точностью до страницы можно помечать память как используемую или как неиспользуемую процессом.

Что ещё интересного нужно отметить в этом красивом решении:
  • Адресное пространство других процессов оказывается за пределами языка. Оно просто не адресуемо, нет таких адресов, которые ссылаются на другой процесс. Чтобы как-то читать/писать в память других процессов нужно специально думать.

  • При попытке обратиться к неиспользуемой памяти, процесс тоже получит от ОС по башке.

To be continue... Следующая серия про x86, а дальше начнётся что-нибудь более интересное



(17 comments) - (Post a new comment)


[info]sweater_judah
2009-03-05 11:21 am UTC (link)
про страничный подход слышал, про сегментный - нет.
благодаря посту прокачал эрудированность, спасибо ( :

(Reply to this) (Thread)


[info]fat_crocodile
2009-03-05 12:18 pm UTC (link)
Абсолютно бесполезное знание :)
Сегментный подход нигде не используется (но было дело, x286 уже умел сегменты, но ещё не умел страницы, соответственно OS/2 должна была на этом как-то работать), тут описан по чисто педагогическим причинам:
-- от него местами торчат хвосты (хотя я надеюсь их избежать)
-- мне казалось, что правильнее описывать развёртывание идеи поэтапно.

(Reply to this) (Parent)(Thread)


[info]sweater_judah
2009-03-05 04:50 pm UTC (link)
само собой, так оно и есть. но люди, которые играют в чгк - как известно - любят знания без приложений.
интересно, где хвосты этого сегментного подхода? о_0

(Reply to this) (Parent)(Thread)


[info]fat_crocodile
2009-03-06 09:21 am UTC (link)
Про хвосты я наверное погорячился.
Но из-за обратной совместимости все возможности 286 остались в новых процессорах, т.е. в x86 есть оба механизма, их пожалуй даже можно использовать одновременно.

(Reply to this) (Parent)


[info]ran_dom
2009-03-05 12:08 pm UTC (link)
а лабораторные ты со студентами делаешь на эту тему? в какой операционке?

(Reply to this) (Thread)


[info]fat_crocodile
2009-03-05 12:14 pm UTC (link)
Нет, не делаю.
Если буду, то в своей :) Есть у меня такой план...

В данном случае речь про разные подходы к одной и той же проблеме на разных процессорах. А этот пост просто вводный, мне проще было написать, чем искать ссылки на хорошее объяснение :)

(Reply to this) (Parent)(Thread)


[info]ran_dom
2009-03-05 12:56 pm UTC (link)
извини, я тебя не понимаю:
>>Если буду, то в своей
а в какой ещё?

>>одной и той же проблеме на разных процессорах
я вначале понял, что ты про защищенный и виртуальный режимы пишешь у x86(соответственно сегментная и страничная защита). ты собираешься сравнивать с другими процессорами?
плюс сегментная и страничная защита стали использоваться почти одновременно (только 186 и 286 не поддерживали виртуальный режим) - новые процы поддерживают сегментную, но ее не используют за ненадобностью (возможно её используют гипервизоры)...

(Reply to this) (Parent)(Thread)


[info]fat_crocodile
2009-03-05 01:20 pm UTC (link)
А к чему тогда вопрос "в какой?" :)
в minix, например.

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

Если ты что-то заметил -- скажи.

(Reply to this) (Parent)(Thread)


[info]ran_dom
2009-03-05 01:45 pm UTC (link)
ааа - своя это не группа а операционка ;)

про режимы (без специфики)- тогда лучше называть их с управляемой и не управляемой памятью (managed memory, memory management unit). из специфики - 4096

(Reply to this) (Parent)(Thread)


[info]fat_crocodile
2009-03-05 01:52 pm UTC (link)
там "к примеру, 4096"

:)

(Reply to this) (Parent)


[info]fat_crocodile
2009-03-06 09:20 am UTC (link)
А 186 вроде вообще только реальный режим поддерживал, нет разве?

(Reply to this) (Parent)


[info]whitelynx
2009-03-19 08:54 pm UTC (link)
У вас есть своя, Вами написанная, операционка? Внушает.
Здорово, что кто-то пишет посты про виртуальную память. Спасибо Вам! Удачного отдыха!

(Reply to this) (Parent)(Thread)


[info]fat_crocodile
2009-03-29 11:22 am UTC (link)
Не, пока нету. Пока только план :)

(Reply to this) (Parent)


[info]scavenger_spb
2009-03-05 06:19 pm UTC (link)
Занятно.

ЗЫ Очепятко бросается в глаза сразу: "Либез"...

(Reply to this) (Thread)


[info]sweater_judah
2009-03-05 11:22 pm UTC (link)
фуф

(Reply to this) (Parent)


[info]fat_crocodile
2009-03-06 09:08 am UTC (link)
фиксед, спасибо

(Reply to this) (Parent)

(Reply from suspended user)

(17 comments) - (Post a new comment)

Create an Account
Forgot your login or password?
Login w/ OpenID
English • Español • Deutsch • Русский…