Все это совпадает с моими взглядами, и я рад, что вы думаете так же.
Некоторые люди задавали вопросы о периодичности выпусков. Могу сказать, что
делать выпуски я буду по мере возможностей, возможно чаще, чем раз в неделю.
Начиная с этого выпуска, я начну углубляться в тонкости архитектуры и
устройства систем, если кому-то что-то становиться непонятно - пишите... Мой
адрес вы найдете в конце письма.
В этом выпуске мы поговорим об архитектуре современных процессоров и о
предоставляемых средствах защиты. Понимание этого будет необходимо нам, когда мы
перейдем непосредственно к программированию операционной системы.
Для начала небольшое предисловие.
В процессорах имеются базовые регистры,
которые могут задавать смещение. На 16-битной архитектуре максимальное смещение
могло быть до 64 килобайт, что, в общем-то, не много и вызывало определенные
трудности (разные модели памяти, разные форматы файлов). Так же, в 16-битной
архитектуре присутствовали сегментные регистры, которые указывали адрес сегмента
в памяти. В процессорах, начиная с i386, базовые регистры стали 32-х битными,
что позволяет адресовать до 4 гигабайт. Сегментные регистры остались 16-битными,
и в защищенном режиме они не содержат адреса! они содержат индекс дескриптора. В
реальном режиме сегментные регистры работают так же, как и на 16-битных
процессорах.
В реальном режиме сегментные регистры непосредственно указывают на адрес
начала сегмента в памяти. Это позволяет нам, без каких либо преград, адресовать
1 мегабайт памяти. Но создает определенные трудности для защиты. А защищать нам
нужно многое. Например, мы не можем пользовательским программам дать возможность
непосредственно обращаться к коду или данным ядра. Так же мы не можем дать
возможность пользовательским программам обращаться к коду или данным других
пользовательских программ, поскольку это может нарушить их
работоспособность.
Для этого был изобретен защищенный режим работы
процессора, который появился в процессорах i286.
Защищенность этого режима заключается в следующем:
Сегментный регистр
больше не указывает на адрес в памяти. В этом регистре теперь задается индекс в
таблице дескрипторов.
Таблица дескрипторов может быть глобальная или
локальная (применяется в многозадачных системах для изоляции адресного
пространства задач) и представляет собой массив записей, по 8 байт в каждой, где
описываются адреса, пределы и права доступа к сегментам.
Про адрес ничего не
буду говорить, и так все ясно. Что такое предел? В этом Поле описывается размер
сегмента. При обращении за пределы сегмента процессор генерирует исключение
(специальное прерывание защищенного режима). Так же исключение генерируется в
случае нарушения прав доступа к сегменту. Поле прав доступа описывает
возможность чтения/записи сегмента, возможность выполнения кода сегмента,
уровень привилегий для доступа к сегменту.
При обращении к сегменту из дескриптора берется базовый адрес сегмента и
складывается со смещением сегмента. Так получается линейный 32-х разрядный (в
i286 - 24-х разрядный) адрес. Для i286 на этом процесс получения адреса
завершается, линейный адрес там равен физическому. Для i386 или выше это
справедливо не всегда.
В процессорах, начиная с i386, появилась, так называемая, страничная
организация памяти. Страница имеет размер 4 килобайта или 4 мегабайта. Большие
страницы могут быть только в pentium или выше. Не знаю только, какой толк от
таких страниц.
Если возможность страничной адресации не используется, то
линейный адрес, как и на i286, равен физическому. Если используется - то
линейный адрес разбивается на три части. Первая, 10-битная, часть адреса
является индексом в каталоге страниц, который адресуется системным регистром
CR3. Запись в каталоге страниц указывает адрес таблицы страниц. Вторая,
10-битная, часть адреса является индексом в таблице страниц. Запись в таблице
страниц указывает физический адрес нахождения страницы в памяти. последние 12
бит адреса указывают смещение в этой странице.
В страничных записях, как и в
дескрипторных записях, есть служебные биты, описывающие права доступа, и
некоторые другие тонкости страниц. Одной из важных тонкостей является бит
присутствия страницы в памяти. В случае не присутствия страницы, процессор
генерирует исключение, в котором можно считать данную страницу из файла или из
swap раздела. Это сильно облегчает реализацию виртуальной памяти. Чуть ниже мы
про это поговорим.
Надеюсь, я не сильно вас утомил? более подробно про все это можно прочитать в книгах по архитектуре процессоров. А мы вернемся к операционным системам.
Многозадачные возможности в процессорах так же появились в процессорах,
начиная с i286. Для реализации этого, процессор для каждой задачи использует,
так называемый, "сегмент состояния задачи" ("Task State Segment", сокращенно
TSS). В этом сегменте, при переключении задач, сохраняются все базовые регистры
процессора, сегменты и указатели стека для трех уровней защиты (для каждого
уровня используется свой стек), сегментный адрес локальной таблицы дескрипторов
("Local descriptor table", сокращенно LDT). В процессорах, начиная с i386, там
еще хранится адрес каталога страниц (регистр CR3). Так же этот сегмент
обеспечивает некоторые другие механизмы защиты, но о них мы пока не будем
говорить.
Операционная система может расширить TSS, и использовать его для хранения
регистров и состояния сопроцессора. Процессор при переключении задач не
сохраняет этого. Так же возможны другие применения.
В своей работе мы не будем ориентироваться на процессор i286, поскольку
16-битная архитектура и отсутствие механизма страничного преобразования сильно
усложняет программирование операционной системы. К тому же, таких процессоров
давно уже никто не использует. :)
Ориентироваться мы будем на i386 или более старшие модели процессоров, вплоть
до последних.
Ядро системы при распределении памяти оперирует 4-х килобайтными
страницами.
Страницы могут использоваться самим ядром, для нужд драйверов
(кэширование, например), или для процессов.
Программа или процесс состоит из следующих частей:
Но, обычно, системы делят сегмент данных на две части: инициализированные
данные и не инициализированные данные.
Все сегменты разбиваются на страницы. Сегмент кода имеет постоянный размер.
Сегмент данных может увеличиваться в сторону больших адресов. Сегмент стека,
поскольку растет вниз, увеличивается в сторону уменьшения адресов. Страницы
памяти для дополнительных данных или стека выделяются системой по мере
необходимости.
Очень интересный момент:
При выполнении программы
операционная система делает следующие действия:
Еще один интересный момент:
Когда в системе загружается
две или более одинаковых программы - нет необходимости для каждой из них
выделять место для кодового сегмента, они спокойно могут использовать один код
на всех.
Ну не знаю что еще написать, жара достала, не знаю как у вас, а у нас в
Москве - за 30... :(
В следующем выпуске мы рассмотрим процессы загрузки разных операционных
систем (Windows не предлагать!). Ну, может быть еще что-то, это я узнаю из ваших
писем.
По всем вопросам вы можете обращаться ко мне по адресу: mailto:dron@infosec.ru?Subject=AsmOS.
При поддержке Kalashnikoff.ru