Страничная организация памяти
в х86 1ое
размер страницы 4k обычно.
Если последовательно записать 1, а потом 4, выделив на оба числа пральное количество бит, получится как раз 1028.
пральное количество битВ этом и разница. По твоей логике получается, что компилятор должен во время компиляции знать размер страницы. Так вот не должен. Насколько я понимаю, размер страницы в процессоре настраивается, и однажды скомпилированная программа нормально работает при разном размере страницы.Поправьте меня если что..
смотря что считать правильным
Вроде бы там есть Выражения, которые дефайнятся нужным образом (тип ОС, тип процессора, адресация, размер страницы) ? Так что компилятор как бы знает а на деле все потом доопределяется.
Товарищи! Вы путаете страницы с сегментами!
Страничная адресация в ОС или не страничная, програма всегда работает с сегментами (чей размер, кстати, задаётся на этапе компиляции а не со страницами. В 32-ух битной ОС длинный указатель имеет формат селектор_сегмента(16):смещение(32). какие тут вопросы?
Ты когда-нибудь видел ненулевой селектор сегмента? Я - нет. Чо за прогон вообще?
[offtop]
Чуве, селектор - это сегментный регистр. Можешь запустить любую из своих поделок, и посмотреть в отладчике, чему равны значения CS, DS, ES и т.д. Так что ты, наверно, про дескрипторы сегментов говоришь. Они тоже с ненулевой базой бывают, в винде, например, это то, на что ссылаецо FS. (Всюду говорим о 32-битном случае).
[/offtop]
Упс. И вправду ненулевые.
А мне всегда казалось, что нулевые. Мб они в дебажных версиях плюсовых прог нулевые, конечно.
Только я не пойму никак, в чём их смысл. А ещё они на сколько сдвигают - по прежнему на 16 байт? Тогда их правильнее называть не сегментными =)
В программе есть линейный адрес, и есть таблица страниц для процесса.
а это от ОСи зависит. Линукс вот вроде не работает. Там вообще страничная адресация преобладает. А сегментов там по дефолту четыре на всю ось что ли. И у всех смещение сегмента нулевое, а размер - на все 4 гига.
On x86 machines, Windows chooses a page size of 4K because that was the only page size supported by that architecture at the time the operating system was designed. (4MB pages were added to the CPU later, in the Pentium as I recall, but clearly that is too large for everyday use.)
For the ia64, Windows chose a page size of 8K. Why 8K?
It's a balance between two competing objectives. Large page sizes allow more efficient I/O since you are reading twice as much data at one go. However large page sizes also increase the likelihood that the extra I/O you perform is wasted because of poor locality.
Experiments were run on the ia64 with various page sizes (even with 64K pages, which were seriously considered at one point and 8K provided the best balance.
Note that changing the page size creates all sorts of problems for compatibility. There are large numbers of programs out there that ly assume that the page size is 4K. Boy are they in for a surprise.
А ещё они на сколько сдвигают - по прежнему на 16 байт? Тогда их правильнее называть не сегментными =)Вообще, речь же про protected mode? Там "все не так", и название "сегментный регистр" скорее получается историческим. Почитай, например, здесь.
Меня вот всегда интересовало, зачем интеловцы в 80286 сделали служебными младшие биты селектора, а не старшие? Можно было бы создавать подряд несколько дескрипторов, и работать с массивами длиннее 64K с помощью простой адресной арифметики.
Ммм. Попытался почитать. Возникло стойкое ощущение, что речь всё-таки про flat mode. Нет?
---
...Я работаю антинаучным аферистом...
Зачем дополнительные сложности какие-то?
ЗЫ: Я вообще этим не развлекался никогда, мб и фигню несу =)
> Зачем дополнительные сложности какие-то?
Например, для того, чтобы ни один ненормальный root
не получал доступа к памяти ядра, минуя последнее.
---
...Я работаю антинаучным аферистом...
эээ типа. Ну, типа. Как бы Виртуальная Память. Вот!
>> Цытата меня>> Процессор сам превращает виртуальные адреса в физические и вызывает методы ОС чтобы вытащить страницу из свопа.
Каждый процесс сидит в своём 4-х гиговом адресном пространстве. Логические адреса транслируются в физические процессором под контролем ядра. В чём проблема-то?
Каждый процесс сидит в своём 4-х гиговом адресном пространстве. Логические адреса транслируются в физические процессором под контролем ядра. В чём проблема-то?Поддерживаю! Кто хочет полных объяснений, могу одолжить на 30 минту книжку под названием "Процессоры Pentium 4, Athlon и Duron" Гука и Юрова, изд. Питер. Советую приобрести или скачать в эл. варианте (если найдёте)
Партия учит нас, что защищённый режим --- это когда работает механизм защиты памяти.
---
...Я работаю антинаучным аферистом...
где это тут написано?
сегментная адресация давно нигде не используется. х86_64 её уже нет совсем.
---
...Я работаю антинаучным аферистом...
термин защищённый режим скорее относится вообще ко всему разделению задач по привилегиям в многозадачной системе.
понятно что при этом должно быть реализована и защита памяти, а как она реализуется это не так важно.
Она как раз организуется сегментацией. Страничное управление памятью предназначено только для свопинга
Так что Fj все правильно говорит.
Хрен! Один и тот же линейный адрес всегда отображается в один и тот же физический, если не производит замену страниц (например при свопинге)
Короче как у нас тут говорят -- ботай матчасть.
а ты знаешь что у каждой задачи своя таблица виртуальной памяти?
Ты не прав, не каждая задача имеет свой каталог страниц - он один и им управляет ОС, ботай матчасть. Цитата: "...Страницы не имеют прямой связи с логической структурой данных или програм... Страничная переадресация может использоваться с сегментацией без каких-либо требований по согласованию границ сегментов и страниц..."
у потоков одного процесса таблица общая - они в одном адресном пространстве работают.
но на каждый процесс своя таблица.
После включения 32bit protected mode появляется возможность управления защищёнными сегментами, которые располагаются в пространстве физических адресов. Это управление обеспечивается глобальной и локальной таблицей дескрипторов сегментов (GDT и LDT соотв.). Сегменты, доступные всем программам ОС помещает в GDT, плюс у кажой программы наличествует LDT к элементам которго может обращаться только эта программа (если конечно копии этого элемента нет ни в GDT, ни в LDT другой задачи). Не это ли имеется ввиду под "таблицей у каждого процесса"?
Теперь ОС может включить ( а может и не включить ) блок страничной переадресации и занести физический адрес таблицы страниц в спец. регистр. После этого все обращения к памяти проходят через блок страничной переадресации.
Пример после включения Protected Mode и Paging:
ОС запускает процесс и отдаёт ему сегмент линейной памяти в 4 Гб начиная с линейного адреса 0x0001:0x00000000 (режим, когда программе выделяется сегмент максимального размера называется "плоским"). В упрощённом варианте в таблицу страниц при этом ОС заносит нужное кол-во страниц и пишет, что они ещё не выделены. Затем при обращении программы в пределах сегмента с селектором 1 по адресу скажем 0x00010000 ОС видит, что такому линейному адресу не соответствует ни одна страница физ. памяти и выделяет её для программы по тому физ. адресу, по которому пожелает. После чего все вызовы в пределах 0x0001xxxx (зависит от размера страницы) аппаратно и прозрачно для процесса перенаправляются на выбранный ОС физический адрес. Ну и т. д.
в твоей замечательной модели с одним каталогом страниц и flat моделью как решается проблема что два процесса могут захотеть один адрес?
регистр cr3 сохраняется в tss, интересно для чего же это сделано?
при операциях с памятью сначала работает сегментный механизм, а потом страничный.
в твоей замечательной модели с одним каталогом страниц и flat моделью как решается проблема что два процесса могут захотеть один адрес?Легко: на уровне сегментов дать доступ к одному и тому же сегменту, например разместив его в GDT
регистр cr3 сохраняется в tss, интересно для чего же это сделано?Зы... и правда сохряняется
Сорри за флуд, мы уже давно оффтоп. Но изначальный вопрос некорректен, имхо
Надо бы вынести в отдельную тему. Тогда мне интересно, а собственно зачем нужно сохранять cr3 и присваивать его каждой задаче? up
Короче, сделай простенькую консольную программу с одной переменной, которой присваивается, например, номер процесса. Программа выводит адрес и значение переменной. Запусти ее в двух экземплярах и убедись, что в обоих программах переменные будут иметь один адрес, но разные значения. Дальше можно добавить еще одну переменную в разделяемую секцию.
Короче вот прога:
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#pragma section("secA", read, write, shared)
DWORD a;
__declspec(allocate("secA"
DWORD b;
int _tmain(int argc, _TCHAR* argv[])
{
b = a = ::GetCurrentProcessId;
while(1) {
printf("&a = %p\na = %d\n&b = %p\nb = %d\n\n", &a, a, &b, b);
::Sleep(2000);
}
return 0;
}
Запусти сначала один экземпляр, потом в второй -- и втыкай до просветления. Попробуй придумать, как такое можно реализовать без использования страничной адресации, где каждый процесс имеет свои каталоги страниц.
Причем из диссама можно убедится, что для работы обеими переменными используется один сегмент (т.е. значение DS не меняется):
b = a = ::GetCurrentProcessId;
004113AE mov esi,esp
004113B0 call dword ptr [__imp__GetCurren0 (4181A0h)]
004113B6 cmp esi,esp
004113B8 call @ILT+310(__RTC_CheckEsp) (41113Bh)
004113BD mov dword ptr [a (417178h)],eax
004113C2 mov eax,dword ptr [a (417178h)]
004113C7 mov dword ptr [b (419000h)],eax
А не вариант, что одно и то же значение DS в разных LDT указывает на разные сегменты?
адрес один но память разная нужна.
а теперь представь что у тебя замаплено в память пара гигабайт файлов.
и такой процесс не один.
вперёд.
>Зы... и правда сохряняется
открытие блин.
>Тогда мне интересно, а собственно зачем нужно сохранять cr3 и присваивать его каждой задаче?
как ни странно - чтоб у каждой задачи было своё адресное пространство.
все стандартные сегменты идут с 0 и размером 4гб. они все одинаковые.
Что значит адрес один? Зачем тебе надо совпадение в разных программах одного линейного адреса?
>открытие блин.
И правда не знал. Приму к сведению
>как ни странно - чтоб у каждой задачи было своё адресное пространство.
Как уже было сказано это прекрасно работает с одной только сегментацией
А не вариант, что одно и то же значение DS в разных LDT указывает на разные сегменты?Вариант конечно, но для этого примера не годится. Если посмотришь на адреса переменных, то увидишь, что они сидят в соседних страницах. Причем одна общая у процессов, а втрорая нет. С учетом, того что работа с ними через один сегмент (селектор в регистре DS в коде не меняется).
>Если посмотришь на адреса переменных, то увидишь, что они сидят в соседних страницах.
Как ты определяешь, что они в соседних страницах?
004113C2 mov eax,dword ptr [a (417178h)]
004113C7 mov dword ptr [b (419000h)],eax
Адреса у а -- 417178h, у b -- 419000h.
Вопрос был "зачем это нужно?"
например чтоб утилизировать больше чем 4гб адрессного пространстава суммарно всеми процессами.
Афтар, не пиши больше.
1) Зачем вообще нужны сегментные регистры в 32-х битном режиме? Насколько я понимаю, в real mode они выполняли две задачи: а) можно было зашивать смещение адресов переменных/джампов прямо в код, б) можно было адресовать аж целый мегабайт памяти. В системе с виртуальной памятью (а) выполняется автоматически, под виндой любая прога видит себя сидящей по адресу 0х40000000 (по дефолту (если я ничего не путаю (б) ну и так четырёх гигов адресного пространства пока вполне хватает.
2) А как они работают? Ну то есть эффективный адрес в real mode получался как offset + segment * 16; а если у тебя оффсет 32битный, а сегмент всего 16, то как-то очень странно всё выглядит - 65536 четырёхгиговых страниц и все начинаются в первом мегабайте. А всё адресное пространство получается 4гб + 1мб. Неаккуратненько!
а там уже записано смещение, размер и права на доступ и тд.
сейчас их никто не использует, и в x84_86 их почти полностью выкосили.
только лишние опреации при каждом вычислении адреса и
проверке прав доступа - всё равно везде поголовно flat mode.
точнее не помню, доставать книжку тоже лень.
что-то подумалось.
вроде с помощью сегментов можно было бы реализовать один не исполняемый стэк без лишенего гемороя - просто cs не натягивать на всё адресное пространство. но наверно и тут есть какие-то проблемы.
В винде в регистре FS сидит селектор дескриптора сегмента, в котором сиди Thread Information Block. Например, FS:[0] указывает на список SEH, а FS:[2C] указывает массив TLS.
ну прочитай ты статью, на которую я ссылку давал - там как раз подробный ответ на оба вопроса (хотя, в большей степени все же на второй).
Систему селекторов/дескрипторов придумали еще для 80286, и там они были реально необходимы, т.к. регистры были 16-разрядными, и адресовать ими без пачки дексрипторов все 16 мегов, доступных 80286 было невозможно. А дальше оставили все как есть немного подправив в 80386 исходя из главной проблемы всей архитектуры x86 - обратной совместимости.
А на мой вопрос есть что-нибудь?
не, не знаю. Где-то дома (не в Москве) валяется старая книжка по OS/2 2.0, в которой примерно половина книги посвящена объяснению что такое protected mode, как оно работает и насколько оно круто, книжка была написана как раз про 80286 еще. Возможно, там был ответ на твой вопрос, но я не помню ни названия этой книжки, ни автора.
Оставить комментарий
stm7868162
Как записываются виртуальные адреса в скомпилированной (для ОС со страничной организацией памяти) программе?Т.е. следующее: размер страницы = 1024
1) В программе виртуальные адреса линейные. На процессор подаём адрес 1028. Он сам вычисляет, что это 1-я страница, смещение 4
Или же
2) В программе виртуальные адреса сразу имеют форму 1:4 (1-я страница, смещение 4)
?