Страничная организация памяти (Зачем?)
Модератор: Модераторы разделов
-
BratSinot
- Сообщения: 812
- ОС: Slackware64
Страничная организация памяти
Доброго времени суток!
Долгое время читал и думал, зачем нужна страничная организация памяти? Были мысли о более простом разделении пространств памяти для процессов в protected mode, но с появлением больших страниц этот вариант отлетает.
Еще была мысль об оперировании бОльшими объемами оперативной памяти (скажем "2^31-1" страниц по "2^31-1" бит в каждой), но даже со страничной памятью, в том-же 32-битном режиме больше 4GiB ни-ни.
Еще есть вариант, что легче задавать "параметры" для частей памяти (RO/RW, QOW и т.д.), но опять-же, с появлением большИх страниц этот вариант тоже не рассматривается.
В итоге, я вижу в страничной организации памяти одни лишь костыли (нужна таблица для этих страниц и т.д и т.п.)
P.S. И попрошу без холиваров и "посылов", по делу.
Долгое время читал и думал, зачем нужна страничная организация памяти? Были мысли о более простом разделении пространств памяти для процессов в protected mode, но с появлением больших страниц этот вариант отлетает.
Еще была мысль об оперировании бОльшими объемами оперативной памяти (скажем "2^31-1" страниц по "2^31-1" бит в каждой), но даже со страничной памятью, в том-же 32-битном режиме больше 4GiB ни-ни.
Еще есть вариант, что легче задавать "параметры" для частей памяти (RO/RW, QOW и т.д.), но опять-же, с появлением большИх страниц этот вариант тоже не рассматривается.
В итоге, я вижу в страничной организации памяти одни лишь костыли (нужна таблица для этих страниц и т.д и т.п.)
P.S. И попрошу без холиваров и "посылов", по делу.
-
NickLion
- Сообщения: 3408
- Статус: аватар-невидимка
- ОС: openSUSE Tumbleweed x86_64
Re: Страничная организация памяти
Страничная организация памяти позволяет упростить многие вещи. Те самые костыли — таблица страниц — позволяет использовать одну физическую станицу в разных виртуальных адресах — это и библиотеки, и программы, которые подгружаются только один раз, несмотря на несколько "копий" в разных процессах. Далее, возможность выгружать неиспольуемые участки в своп. Изоляция памяти для разных процессов. fork() и COW для экономии.
Спасибо сказали:
-
BratSinot
- Сообщения: 812
- ОС: Slackware64
Re: Страничная организация памяти
Ну а что мешает делать это без страниц? Вместо номера страницы, смещения и длинны, мы укажем начало и конец участка памяти.
Да и эти библиотеки разве не дублируются? Разве для этого не требуется какой-нибудь KMS/UKMS?
Я уже об этом упоминал в шапке. Опять-же, чем мешает указывать по два числа, начало и конец? И опять-же вспоминаем про большие страницы. Получается, если у меня страница будет 1GiB то любой процесс минимум 1GiB займет что-ли?
Я могу ошибаться, но страничная память вроде появилась и стала использоваться раньше, чем подобия protected mode?
-
VAA
- Сообщения: 224
- ОС: Deep Style / Slackware
Re: Страничная организация памяти
BratSinot писал(а): ↑26.01.2013 01:24
Ну а что мешает делать это без страниц? Вместо номера страницы, смещения и длинны, мы укажем начало и конец участка памяти.
Да и эти библиотеки разве не дублируются? Разве для этого не требуется какой-нибудь KMS/UKMS?
Я уже об этом упоминал в шапке. Опять-же, чем мешает указывать по два числа, начало и конец? И опять-же вспоминаем про большие страницы. Получается, если у меня страница будет 1GiB то любой процесс минимум 1GiB займет что-ли?
Я могу ошибаться, но страничная память вроде появилась и стала использоваться раньше, чем подобия protected mode?
Уважаемый BratSinot! Вопрос ваш не очень ясно и конкретно сформулирован. Правильно ли я понял, что вы обсуждаете организацию виртуальной памяти в процессорах семейства i386?
Ныне это своего рода стандарт. До его появления архитектура виртуальной памяти была опробована в массе вариантов как с точки зрения решаемой задачи, так и с точки зрения деталей технической реализации.
И действительно, первой задачей была не реализация protected mode, а оптимизация согласования размеров физической памяти и требуемого для задачи адресного пространства. Но и защита разных задач друг от друга потребовалась почти одновременно.
С точки зрения решаемой задачи возможно две ситуации:
- архитектура исторически была с небольшим адресным пространством а физическая память стала большой. Классический вариант: PDP-11 c 16-разрядным адресным пространством получила физическую память с адресом 18-разрядным, а потом и 22-разрядным. При этом виртуальная память решала задачу размещения в физической памяти одновременно большого количества небольших задач. Такая реализация и породила классический юниксовый подход - задача должна быть простой и решать одну функцию,
- архитектура имела адресное пространство, заведомо большее, чем физическая память на момент ее зарождения и позволяла аппаратно организовывать свопинг прозрачно для программиста (без ручной подкачки оверлеев). Классический пример - IBM370 с 24(или 32) разрядным виртуальным пространством во времена, когда и 128Кбайт памяти было много.
Первоначальная архитектура I8086 предусматривала 1 Мбайт адресного пространства. Виртуальная память i386 вобрала опыт многочисленных предыдущих вариантов реализации в других архитектурах и оказалась достаточно гибкой, позволяющей реализовать сразу несколько принципиальных подходов. Прожила без изменений достаточно долго, пока физическая память больше 4 гигабайт стала доступной и адресное пространство вышло за 32 разряда.
Хочу отметить, что организация виртуальной памяти не может усложняться произвольно. Это компромис между програмной гибкостью и временными потерями на преобразование виртуального адреса в физический. И одним из средств достижения компромиса является страничная организация памяти.
Если хочется хорошенько подумать над этим вопросом - поищите в интернете различные варианты реализации архитектуры виртуальной памяти. Правда я не уверен, что сейчас возможно найти ранние варианты, среди которых точно были и аналогичные тем, которые вы попытались придумать.
Например: В PDP-11 адресное пространство 64 Кбайт делилось на 8 страниц по 8 Кбайт с фиксированными в виртуальном адресном пространстве адресами. По тем временам 8 Кбайт было довольно много. В машине типа СМ3 вся физическая память могла составлять 16 Кбайт в минимальной поставке (а максимум - 56 Кбайт
Registered Linux user number 436365
-
drBatty
- Сообщения: 8735
- Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
- ОС: Slackware-current
Re: Страничная организация памяти
это тоже будут такие «страницы». только размером в 1 байт. В силу того, что такими количествами память мерить не эффективно, используют страницы в 4К.
т.е. не эффективно использовать один байт в памяти, а второй в свопе (и третий опять в памяти). Лучше увеличить гранулярность.
Спасибо сказали:
-
NickLion
- Сообщения: 3408
- Статус: аватар-невидимка
- ОС: openSUSE Tumbleweed x86_64
Re: Страничная организация памяти
1. Что делать, если в памяти располагаются программки с адресным зазором между ними. Свободная память есть, но нет ни одного участка, способного вместить новую программу. Придётся все программы сдвигать. Представляете насколько это затратно.
2. Динамическая память выделяется во время работы программы. Т.е. программа может расти. Куда ей расти, если она "упёрлась" в следующую за ней? Опять же куда-то кого-то сдвигать, многомегабайтные копирования и тормоза.
Что за KMS/UKMS? KMS знаю только kernel mode setting, которое за отображение отвечает.
Библиотеки дублируются только виртуально. Т.е. пусть есть 3 запущенных процесса. 2 – A и 1 – B. Также они все используют библиотеку C. Получится, что первый процесс содержит код A, C, второй A, C, третий B, C. В физической памяти располаются по одной копии A (напр. страницы 1, 2, 3), B (5, 6), C (4, 7). Виртуальные же простанства процессов просто содержат указатели на страницы в физической памяти.
P1: 1, 2, 3, 4 ,7, 8
P2: 1, 2, 3, 4, 7, 9
P3: 5, 6, 4, 7, 10
Таким образом дублируются только номера страниц в таблицах, но не сам код. Страницы 8 – 10, это частные данные процессов, которые не шарятся. Кстати, shared memory тоже реализуется через общие номера в таблице. Более того, после вызова fork(), даже частные данные могут разделяться до первой записи одним из процессов. Тот самый COW, о котором упоминал.
Можно. Есть модели сегментной адресации. Там сегменты могут иметь не постоянный размер. По факту, сейчас используется сегментно-страничная модель памяти. Сегменты кратны страницам. Процесс ничего не знает о сегментах, это забота ядра.
Постоянный размер страницы позволяет упростить задачи перемещения страниц (память–своп).
Да. Точнее любая программная единица будет занимать объем кратный 1GiB, т.е. если программа использует 2 разделяемые библиотки, то процесс будет занимать минимум 3GiB. Поэтому таких страниц никто не делает.
Да. Страничная организация памяти один из самых эффективных по скорости методов преодоления проблем фрагментации свободного места памяти (при условии RAM, что выполняется). Посмотрите, HDD/SSD тоже используют сектора.
Спасибо сказали:
-
BratSinot
- Сообщения: 812
- ОС: Slackware64
Re: Страничная организация памяти
Ой, перепутал KMS и KSM. Т.е. KSM/UKSM.
Понятно.
Ладно, с современными системами понятно, а что делать со старыми? Просто страницы же появились еще до того, как появилась многозадачность и т.п., или я ошибаюсь? Если нет, то зачем оно тогда нужно было?
-
DaemonTux
- Сообщения: 1480
- Статус: Юный падаван
- ОС: Gentoo
Re: Страничная организация памяти
есть замичательная книжка называется: Что должен знать программист о памяти. Там будут ответы на ваши вопросы.
Vladivostok Linux User Group
-
BratSinot
- Сообщения: 812
- ОС: Slackware64
-
VAA
- Сообщения: 224
- ОС: Deep Style / Slackware
Re: Страничная организация памяти
В общем случае это неверно. Привязано к реализации в i386, где физическая страница фиксированного размера и размещается на границе, кратной размеру страницы, а переменная длина организуется объединением нескольких физических страниц. Всегда кратна длине одной страницы и меньше одной не может быть.
В других системах использовались и страницы переменной длины - смотри мой пример для PDP-11, оно же см4 с 18-разрядным или СМ1420 с 22-разрядным физическим адресом.
В PDP-11 длина страницы и ее адреса в физической памяти (начальный и конечный) могли изменяться с дискретностью в 64 байта. Не один байт, конечно, чтобы не усложнять оборудование.
P.S. Книжка подразумевалась эта http://rus-linux.net/lib.php?name=/MyLDP/h...memory-4-1.html ? Она вроде ориентирована на i386 без исторического обзора.
Еще полезно http://ru.wikipedia.org/wiki/Виртуальная_память Смотри то, что там названо сегментной адресацией.
Registered Linux user number 436365
-
DaemonTux
- Сообщения: 1480
- Статус: Юный падаван
- ОС: Gentoo
Re: Страничная организация памяти
VAA писал(а): ↑27.01.2013 01:31P.S. Книжка подразумевалась эта http://rus-linux.net/lib.php?name=/MyLDP/h...memory-4-1.html ? Она вроде ориентирована на i386 без исторического обзора.
вот более точная ссылка
Vladivostok Linux User Group
-
drBatty
- Сообщения: 8735
- Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
- ОС: Slackware-current
Re: Страничная организация памяти
для расширения размера памяти. Если у вас адрес в 16 бит, то вы физически не сможете адресовать больше 65536 байт. Потому выделялось окно, которое можно было переключать на разные куски памяти. Затем в 8086 ввели ещё один регистр адреса (тоже 16 бит), содержимое которого сдвигалось на 4 бита влево и складывалось с основным. Т.о. стало возможно адресовать 16*64К = 1048576 байтов. (ms-dos представляла 10 таких сегментов по 64K, откуда взялось выражение "640К хватит всем")
согласен.
Спасибо сказали:
-
BratSinot
- Сообщения: 812
- ОС: Slackware64
Re: Страничная организация памяти
Отлично, с этим все совсем понятно. Только вот отсюда возникает вполне резонный вопрос, какого черта этого нет на x86 в 32-битном режиме? Даже тот-же PAE просто использует 36-битную адресацию. Почему нельзя использовать те самые два регистра (CS/IP) по полной? Один на номер страницы, итого 2^32-1 страниц, и на каждую страницу можно 2^32-1 бит выделить.
-
drBatty
- Сообщения: 8735
- Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
- ОС: Slackware-current
Re: Страничная организация памяти
зачем? это уже история, дела давно минувших дней.
где вы видели код такого громадного размера? для данных вполне хватает 2³² байта, а если не хватает, то 64х битная архитектура уже готова, причём в каждом доме.
эти страницы - костыль. Намного проще и быстрее адресовать плоские 2⁶⁴ байта памяти. Зачем нужны костыли?
Поймите простую вещь: сейчас страницы используются не как костыль, а для другого - для изменения свойств памяти. Что-бы знать, какая память данных, какая констант, какая кода, а какой памяти вообще не существует физически. К примеру, выделяя 100500 байт памяти, на самом деле ничего НЕ выделяется. Это очень упрощает код - выделение памяти происходит только в том случае, если эта память _действительно_ используется. За счёт этого достигается сильное увеличение быстродействия, а также расширяемость: программа может выделить 1Гб памяти для себя, и их свободно использовать. Это будет работать и на слабых системах (мало памяти - мало данных, но код такой-же), и на сильных. Всё это конечно можно решить и программно, но железный подход как обычно в разы быстрее.
Конечно остаётся вопрос размера страниц. Очевидно, что 1 байт - слишком мелкая величина. Потому остановились на 4К, которые сегодня являются стандартом. Ну и конечно это ещё и размер блока многих ФС.
ЗЫЖ всё это верно для x86, про другие - либо не знаю(ARM), либо знаю(PDP, etc), но уже устарело.
Спасибо сказали:
-
NickLion
- Сообщения: 3408
- Статус: аватар-невидимка
- ОС: openSUSE Tumbleweed x86_64
Re: Страничная организация памяти
На ARM, есть как с MMU (читай страничная виртуальная память), так и без MMU (простая плоская память без страниц и защиты) — микроконтроллеры всякие, где не нужно.
-
arturpub
- Сообщения: 5
Re: Страничная организация памяти
Немного истории не повредит. В начале был 8086 (и его младший брат 8088). У него была шина адреса 20 бит, что позволяло адресовать ровно 1 мегабайт памяти. Шина данных и разрядность регистров общего назначения была 16 бит. Чтобы адресовать все доступное пространоство, интел предложил использовать пары сегментный регистр : регистр смещения, например DS:BX. Ну или CS:IP. Значение сегментного регистра, как здесь уже упоминалось, просто сдвигалось на 4 бита влево, и складывалось со смещением. Можно заметить, что некоторые пары могут вызвать переполнение, как например FFFF:FFFF -> 10FFEF. В этом случае лишние старшие биты просто отбрасывались.
В 80286 появился "защищенный режим". Главным его отличием (в рамках текущего обсуждения) было то, что сегментный регистр содержал уже не адрес, а селектор (т.е. индекс) в "таблицу дескрипторов". Эта таблица лежала в памяти по адресу, который хранился в регистре GDTR. Всякий раз, когда сегментному регистру присваивалось новое значение, процессор обращался (!!!) к этой таблице и загружал из нее в теневой сегментный регистр "дескриптор сегмента". Что это? Это база (24 бит), предел (24 бит), и права доступа. То есть сегмент имел начало и произвольную длину. Обращение за предел сегмента вызывало "segmentation fault".
В 80286 же и появилась многозадачность. Причем тут она? Каждая задача (т.е. процесс или поток) имела свой стек, свой сегмент данных, и свой сегмент кода. Переключение задач, а происходило это обычно по таймеру, приводило к сохранению всех регистров в специальный "сегмент задачи", а также к загрузке всех регистров из той задачи, на которую шло переключение. Т.о. десятки, а то и сотни раз в секунду все сегментные регистры меняли свое значение, что ну очень плохо сказывалось на производительности.
В 80386 эта проблема (и не только эта) была решена вводом страничной организации. Сегментная адресация все еще работала, но уже поверх страничной. Казалось бы, в чем разница, ведь опять же есть двухуровневый каталог страниц? Разница в том, что размер страницы фиксирован, а значит можно буферизовать трансляцию адресов, положив наиболее часто используемые смещения в TLB-буфер. Теперь, если не использовать сегментную организацию памяти, а только страничную, переключения контекста перестали быть настолько дорогими, т.к. трансляции самых активных страниц всегда были в буфере. Теневая часть стала огромной, размером с буфер, и независимой от конкретной задачи.
Как только 80386 реализовал страничную организацию памяти, ВСЕ мейнстримные операционные системы отказались от сегментного преобразования в пользу страничного. В x86-64 сегментное преобразование было окончательно упразднено.
В 80286 появился "защищенный режим". Главным его отличием (в рамках текущего обсуждения) было то, что сегментный регистр содержал уже не адрес, а селектор (т.е. индекс) в "таблицу дескрипторов". Эта таблица лежала в памяти по адресу, который хранился в регистре GDTR. Всякий раз, когда сегментному регистру присваивалось новое значение, процессор обращался (!!!) к этой таблице и загружал из нее в теневой сегментный регистр "дескриптор сегмента". Что это? Это база (24 бит), предел (24 бит), и права доступа. То есть сегмент имел начало и произвольную длину. Обращение за предел сегмента вызывало "segmentation fault".
В 80286 же и появилась многозадачность. Причем тут она? Каждая задача (т.е. процесс или поток) имела свой стек, свой сегмент данных, и свой сегмент кода. Переключение задач, а происходило это обычно по таймеру, приводило к сохранению всех регистров в специальный "сегмент задачи", а также к загрузке всех регистров из той задачи, на которую шло переключение. Т.о. десятки, а то и сотни раз в секунду все сегментные регистры меняли свое значение, что ну очень плохо сказывалось на производительности.
В 80386 эта проблема (и не только эта) была решена вводом страничной организации. Сегментная адресация все еще работала, но уже поверх страничной. Казалось бы, в чем разница, ведь опять же есть двухуровневый каталог страниц? Разница в том, что размер страницы фиксирован, а значит можно буферизовать трансляцию адресов, положив наиболее часто используемые смещения в TLB-буфер. Теперь, если не использовать сегментную организацию памяти, а только страничную, переключения контекста перестали быть настолько дорогими, т.к. трансляции самых активных страниц всегда были в буфере. Теневая часть стала огромной, размером с буфер, и независимой от конкретной задачи.
Как только 80386 реализовал страничную организацию памяти, ВСЕ мейнстримные операционные системы отказались от сегментного преобразования в пользу страничного. В x86-64 сегментное преобразование было окончательно упразднено.
-
drBatty
- Сообщения: 8735
- Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
- ОС: Slackware-current
Re: Страничная организация памяти
в данном конкретном случае как раз повредит.
-
arturpub
- Сообщения: 5
Re: Страничная организация памяти
Чем может повредить история, добрый человек? Мораль, которую можно вынести из развития моделей адресации в х86, звучит так: страничное преобразование более эффективно сегментного. Что вы имеете с этим поспорить?
-
drBatty
- Сообщения: 8735
- Статус: GPG ID: 4DFBD1D6 дом горит, козёл не видит...
- ОС: Slackware-current
Re: Страничная организация памяти
страничная и сегментная организация памяти - ненужные костыли. Конечно они нужны, инвалидам.
конечно инвалидная коляска лучше костыля. И тем не менее, она ещё более вредна для здорового человека, чем костыли. Даже сам Линус и то в плену предрассудков - он убеждён, что виртуальной памяти _должно_ быть БОЛЬШЕ реальной. НАМНОГО больше. Почему - внятно объяснить не может.
А ведь виртуальная память - костыль, с тех далёких времён, когда память была на вес золота. Сейчас иная ситуация - в самой зачуханной рабочей станции большинство памяти тупо НЕ ИСПОЛЬЗУЕТСЯ. Мало того, если костыль - страничная память начинает действительно _работать_, и система что-то ненужное пихает в своп, то ВСЁ, компьютер попросту отказывается работать. Спрашивается: зачем тогда своп и виртуальная память вообще нужна?
-
arturpub
- Сообщения: 5
Re: Страничная организация памяти
drBatty писал(а): ↑23.02.2013 02:10А ведь виртуальная память - костыль, с тех далёких времён, когда память была на вес золота. Сейчас иная ситуация - в самой зачуханной рабочей станции большинство памяти тупо НЕ ИСПОЛЬЗУЕТСЯ. Мало того, если костыль - страничная память начинает действительно _работать_, и система что-то ненужное пихает в своп, то ВСЁ, компьютер попросту отказывается работать. Спрашивается: зачем тогда своп и виртуальная память вообще нужна?
Страничное преобразование это удобный способ отмапить какие-то данные в конкретное место в адресном пространство задачи. Это как локальные координаты в сложной геометрической системе. А своппинг вообще отношения не имеет к страницам. В своем дескрипторе страница может быть помечена как "неприсутствующая", то есть по ее адресу нет никакой памяти вообще. При обращении к данным внутри нее происходит page fault, критичиская ошибка. И только операционная система, реализующая своппинг, понимает, что ребята все нормально, ждите, поднимает страницу с диска, и мапит ее по локальному опять же относительно задачи адресу, и перезапускает инструкцию, которая вызвала фолт.
Своп это не более чем способ избежать краха задачи временными тормозами. То, что вы привыкли к постоянному своппингу на рабочих станциях, говорит лишь о том, что у вас дурацкая ОС, но она видимо стоит того. Фотошоп наверное только на ней работает, или еще что полезное. В нормальных (серверных) системах всегда следят за использованием свопа, и, если он часто используется, добавляют памяти, или снижают нагрузку к-л путем, например увеличением кластера, frontend-кешированием и т.п.
Страничная организация -- это способ дать каждой задаче псевдо-собственное полное адресное пространство, без оглядки на другие задачи системы. Полная изоляция задач -- вот ключевая цель виртуализации памяти. Сегментная организация давала то же самое (неприсутствие сегмента тоже было возможно), но страдала от собственной динамичности, которую так хочет ОП.
ps. Ну и я не видел случаев, когда система (кроме виндовс конечно), скидывая часть задачи в своп, отказывалась потом работать. Не перегнул ли ты?
-
arturpub
- Сообщения: 5
Re: Страничная организация памяти
Не знаю, что там у Линуса не получается объяснить, но большое виртуальное адресное пространство снимает многие ограничения. Например можно отмапить в память 10-гигабайтный(-ые) файл(-ы) базы данных (не выкачивая их с диска, а просто сказав механизму своппинга, что вот эти страницы лежат как бы в этих файлах, надо будет подгрузишь). И работать с ними напрямую, а не через эту замочную скважину под названием read-write-копирую-задорого-туда-сюда. Просто обращаешься к памяти, а данные сами подкачиваются. В системе кончается память? Ха, можно просто обнулить те страницы, которые только читались, и отдать их другой задаче, ведь изначальные данные и так уже на диске. Попробуй-ка это сделать, когда ты засосал кусок базы обычным read'ом к себе в выделенную обычным способом память! Сейчас, когда мы перешли наконец на 64-бита, это стало возможно. Возможно в будущем размеры баз станут такими, что и 64 бита будет мало.