Comments 67
то что надо! Но зачем начинать писать ОС в 12-й ночи на понедельник?
буду читать завтра на работе :)
буду читать завтра на работе :)
+6
Выложили сразу как дописали, чтобы не забыть. Читайте на здоровье, будем отвечать по возможности.
+6
За интересным делом часто забываешь о времени :)
+6
а когда еще есть время? только по выходным.
0
UFO just landed and posted this here
UFO just landed and posted this here
Мне в прошлый раз не ответили, она будет новая-то? Ну, принципиально, в смысле
-15
я, например, не могу понять, что Вы имеете ввиду.
принципиально новая — это, по идее, важная, с важными новшествами.
в данном топике рассматривают пример СОЗДАНИЯ оси. конечно же, это не Windows или Linux. это что-то свое, возможно хуже, возможно лучше существующих систем, но это не важно. важно научится и попробовать сделать. а дальше сам старайся сделать «принципиально новое».
принципиально новая — это, по идее, важная, с важными новшествами.
в данном топике рассматривают пример СОЗДАНИЯ оси. конечно же, это не Windows или Linux. это что-то свое, возможно хуже, возможно лучше существующих систем, но это не важно. важно научится и попробовать сделать. а дальше сам старайся сделать «принципиально новое».
-1
Еды нет, уходите.
+1
>> jmp $; зависаем
Блин… я и завис… Assembler для меня пуще магии… О_о
Нихрена не понятно, смещения, сдвиги, но всегда выглядит эффектно!.. )
Блин… я и завис… Assembler для меня пуще магии… О_о
Нихрена не понятно, смещения, сдвиги, но всегда выглядит эффектно!.. )
+2
Только сегодня вспоминал о вашем цикле, куда пропали типа. Завтра читать буду
+1
А я уже перестал надеятся на продолжение. Спасибо огромное! Продолжайте писать!
+1
jmp $ — некрасиво. Раз уж у нас IOPL=0, почему бы не использовать cli-hlt? Или не сделать то, что не умели делать DOS и не выключить компьютер программно?
+3
my bad, не то пытался вставить. Выключить компютер вот так:
mov ax, 5307h
mov bx, 1
mov cx, 3
int 15h
mov ax, 5307h
mov bx, 1
mov cx, 3
int 15h
+1
«Однако, обратившись, например, по адресу FFFF:FFFF, можно “прыгнуть” немного выше этой планки, и полученный адрес будет иметь длину 21 бит»
Здесь автор обошел такую интересную вещь как HMA, 64кб для вирусов и резидентов реального режима 80286+ процессора )) Это сейчас кажется не важным, а 15 лет назад еще +64Кб памяти считалось искусством.
Здесь автор обошел такую интересную вещь как HMA, 64кб для вирусов и резидентов реального режима 80286+ процессора )) Это сейчас кажется не важным, а 15 лет назад еще +64Кб памяти считалось искусством.
+2
+3
А при чем здесь своя ОС? Статья-то об адресации памяти процессоров Intel
-2
Вы наверное пропустили первую часть
+1
Читал, но изучение ассемблера и разработка ОС это разные вещи.
-4
разработка ОС напрямую связана со знанием ассемблера
0
Какая часть, скажем, Линукса написана на ассемблере? Одна тысячная?
-2
Согласитесь, сравнивать нашу учебную ОС с нынешними версиями линукса немного не корректно, ведь правда? И если уж вы заговорили о линуксе, то давайте возьмём раннюю версию ядра и посмотрим. Чтобы не тратить ваше время приведу результаты подсчётов:
строк на ассемблере: 1464
строк на C: 8413
А это далеко не одна тысячная.
строк на ассемблере: 1464
строк на C: 8413
А это далеко не одна тысячная.
+1
Я использовал верcию 0.01. Скачать можно <a hrеf=«www.kernel.org/pub/linux/kernel/Historic/»>тут
+1
Согласитесь, сравнивать нашу учебную ОС с нынешними версиями линукса немного не корректно, ведь правда?
Договорились, разработка учебной ОС связана со знанием ассемблера.
-3
Да ладно вам, ни один курс об операционных системах невозможен без описания адресации памяти.
+1
А что в конце получим, DOS?
0
Несколько слов об особенностях обработки прерываний.
Есть 16 аппаратных прерываний IRQ0-IRQ15, которые возникают, когда периферийные устройства типа клавиатуры, таймера или хард-диска пытаются нам что-то сказать. Контроллер прерываний эти сигналы подхватывает и вызывает какие-нибудь INT'ы процессора.
По дефолту IRQ0 — IRQ7 посажены на INT 08h — INT 0Fh, а IRQ8 — IRQ15 на INT 70h — INT 77h. В BIOS'е на них висят обработчики, которые позволяют операционной системе в реальном режиме откликаться на внешние раздражители.
В защищенном режиме INT 08h — INT 0Fh вызываются процессором при возникновении различных исключительных ситуаций (ошибка стека, исключение общей защиты и прочее). Так что контроллер прерываний нужно будет перепрограммировать, чтобы он в ответ на IRQ вызывал другие INT'ы.
Например, если вы хотите живую клавиатуру (IRQ 1), её следует перебросить с INT 09h (который отвечает за Coprocessor Segment Overrun) на какой-нибудь INT в области от 20h до FFh.
Об адресной линии A20.
На современном железе она не обязательно управляется портом 92h, так что приведенный код
in al, 0x92
or al, 2
out 0x92, al
может и сбойнуть.
Как с этим жить
В BIOS есть функция, которая позволяет абстрагироваться от обеих проблем. Это функция 89h прерывания INT 15h, она переключает процессор в защищенный режим, заодно открывает A20 и перепрограммирует контроллер прерываний.
Формат вызова следующий.
Вход:
AH = 89h
BL = номер прерывания для IRQ0, должен быть кратен 8 (IRQ1-7 сядут на следующие 7 прерываний)
BH = номер прерывания для IRQ8, должен быть кратен 8 (IRQ9-F сядут на следующие 7 прерываний)
ES:SI -> адрес GDT для защищенного режима.
Выход:
CF — признак ошибки
CF = 1, AH = FFh — не получилось открыть адресную линию A20
CF = 0, AH = 00h — всё нормально.
Выход из INT 15h будет как обычно, на следующую команду за вызовом. Только мы окажемся уже в защищенном режиме, так что для текущего значения CS должен быть настроен дескриптор. Значение BP не сохраняется, значения всех сегментных регистров будут изменены.
Есть 16 аппаратных прерываний IRQ0-IRQ15, которые возникают, когда периферийные устройства типа клавиатуры, таймера или хард-диска пытаются нам что-то сказать. Контроллер прерываний эти сигналы подхватывает и вызывает какие-нибудь INT'ы процессора.
По дефолту IRQ0 — IRQ7 посажены на INT 08h — INT 0Fh, а IRQ8 — IRQ15 на INT 70h — INT 77h. В BIOS'е на них висят обработчики, которые позволяют операционной системе в реальном режиме откликаться на внешние раздражители.
В защищенном режиме INT 08h — INT 0Fh вызываются процессором при возникновении различных исключительных ситуаций (ошибка стека, исключение общей защиты и прочее). Так что контроллер прерываний нужно будет перепрограммировать, чтобы он в ответ на IRQ вызывал другие INT'ы.
Например, если вы хотите живую клавиатуру (IRQ 1), её следует перебросить с INT 09h (который отвечает за Coprocessor Segment Overrun) на какой-нибудь INT в области от 20h до FFh.
Об адресной линии A20.
На современном железе она не обязательно управляется портом 92h, так что приведенный код
in al, 0x92
or al, 2
out 0x92, al
может и сбойнуть.
Как с этим жить
В BIOS есть функция, которая позволяет абстрагироваться от обеих проблем. Это функция 89h прерывания INT 15h, она переключает процессор в защищенный режим, заодно открывает A20 и перепрограммирует контроллер прерываний.
Формат вызова следующий.
Вход:
AH = 89h
BL = номер прерывания для IRQ0, должен быть кратен 8 (IRQ1-7 сядут на следующие 7 прерываний)
BH = номер прерывания для IRQ8, должен быть кратен 8 (IRQ9-F сядут на следующие 7 прерываний)
ES:SI -> адрес GDT для защищенного режима.
Выход:
CF — признак ошибки
CF = 1, AH = FFh — не получилось открыть адресную линию A20
CF = 0, AH = 00h — всё нормально.
Выход из INT 15h будет как обычно, на следующую команду за вызовом. Только мы окажемся уже в защищенном режиме, так что для текущего значения CS должен быть настроен дескриптор. Значение BP не сохраняется, значения всех сегментных регистров будут изменены.
+3
UFO just landed and posted this here
Здесь, например. Список полезной для разработчиков ОС литературы мы привели еще в первой статье, возможно найдете там еще что-нибудь интересное.
0
Самые широко известные — INT10h для BIOS, INT 21h для DOS — с большой достоверностью гуглятся. А развивающиеся — такие, как дисковый сервис INT 13h, или расширенный AT-сервис INT 15h — по ним информация устаревает быстро. По кускам приходится собирать. Русскоязычные источники вообще, кажется, застряли в 1991-м году.
0
Пипец! Всю башку сломал читаючи.
-1
очень интересно. ждал эту часть и жду следующую
0
спасибо. хорошая и понятная статья. прямо вспомнил лекции николая ивановича корсунова.
хотя на них я ни черта не мог понять и изучал это по книге о ассемблере зубкова
хотя на них я ни черта не мог понять и изучал это по книге о ассемблере зубкова
0
Я еще искал в сети немного другую инфу, как работать с виртуальным 86 режимом.
Никто толком не знает как можно запустить несколько «реальных» режимов и работать с ними
А еще более непонятно как например виндовс 3х грузит досовские дрова реального режима, например сидирома но потом с ними работает в защищенным режиме и такоже подключает сидюк виртуальным 86 режимам — сеансам мс-дос
Такое даже виндовс 9х умеет, работать с драйверами для дос
Никто толком не знает как можно запустить несколько «реальных» режимов и работать с ними
А еще более непонятно как например виндовс 3х грузит досовские дрова реального режима, например сидирома но потом с ними работает в защищенным режиме и такоже подключает сидюк виртуальным 86 режимам — сеансам мс-дос
Такое даже виндовс 9х умеет, работать с драйверами для дос
0
Ну это довольно известная вещь. Вот, например:
osdev.berlios.de/v86.html
osdev.berlios.de/v86.html
0
Примерно то же самое я читал в учебнике по ассемблеру. У меня сегменты вызывают разрыв шаблона. Я пишу на языках высокого уровня и привык к страничной модели памяти. Я знаю как устроен менеджер памяти в Windows, знаю про адресные пространства процессов, про ошибки станиц и подкачку, про отображаемые в память файлы. Но когда я дизассемблером открываю исполняемый файл и вижу mov cs, ds моё понимание напрочь отказыват… Может поясните?
0
В общих чертах, оно вот как обстоит.
Сегмент — это произвольный кусок памяти, у него есть начальный адрес. Этот адрес хранится в сегментном регистре. Адреса объектов внутри сегмента задаются относительно этого базового адреса. Теоретически, можно сделать плоскую память, задать все сегментные адреса в 0 и не париться (что в Windows в пользовательском режиме и сделано).
Если память не плоская, тогда код процесса пихается в свой сегмент; данные пихаются в один или два сегмента. Стек тоже в свой сегмент. Хотя можно всё запихнуть в один и тот же.
DS и ES — сегментные регистры. В них адреса сегментов со статическими переменными и строковыми константами. Команды пересылки данных (кроме стековых) берут базовый адрес из этих регистров.
SS — сегментный регистр, в нем адрес стека. На него ориентируются команды PUSH, POP и команды пересылки, в которых адрес операнда задан в регистре BP или SP.
FS — сегментный регистр. В его сегменте под Windows лежит инфа про SEH-chain, а вообще его можно как угодно использовать.
CS — сегментный регистр, в нем адрес сегмента кода. Адрес CS:IP указывает на текущую выполняемую команду (значение CS своё для каждого потока).
Значения DS и ES задаются свои для каждого процесса. Это позволяет данным от разных процессов лежать рядышком в памяти без использования страничной адресации.
Значение SS — своё для каждого потока. Это позволяет иметь каждому потоку свой стек.
mov cs, ds — если б так можно было делать, привело бы к спонтанной передаче управления неведомо куда. Но в cs вообще нельзя напрямую писать, только командами перехода.
Сегмент — это произвольный кусок памяти, у него есть начальный адрес. Этот адрес хранится в сегментном регистре. Адреса объектов внутри сегмента задаются относительно этого базового адреса. Теоретически, можно сделать плоскую память, задать все сегментные адреса в 0 и не париться (что в Windows в пользовательском режиме и сделано).
Если память не плоская, тогда код процесса пихается в свой сегмент; данные пихаются в один или два сегмента. Стек тоже в свой сегмент. Хотя можно всё запихнуть в один и тот же.
DS и ES — сегментные регистры. В них адреса сегментов со статическими переменными и строковыми константами. Команды пересылки данных (кроме стековых) берут базовый адрес из этих регистров.
SS — сегментный регистр, в нем адрес стека. На него ориентируются команды PUSH, POP и команды пересылки, в которых адрес операнда задан в регистре BP или SP.
FS — сегментный регистр. В его сегменте под Windows лежит инфа про SEH-chain, а вообще его можно как угодно использовать.
CS — сегментный регистр, в нем адрес сегмента кода. Адрес CS:IP указывает на текущую выполняемую команду (значение CS своё для каждого потока).
Значения DS и ES задаются свои для каждого процесса. Это позволяет данным от разных процессов лежать рядышком в памяти без использования страничной адресации.
Значение SS — своё для каждого потока. Это позволяет иметь каждому потоку свой стек.
mov cs, ds — если б так можно было делать, привело бы к спонтанной передаче управления неведомо куда. Но в cs вообще нельзя напрямую писать, только командами перехода.
0
Спасибо. Ну cs ds я от балды написал, просто для примера. Сейчас попробую осмыслить, меня вообще сегментный вопрос давно интересовал, но т.к. с ассемблером я не работаю и были более насущные проблемы, я так для себя его и не прояснил.
Как я понимаю, сегменты появились изначально, до страниц, как способ сэкономить на размерах операндов команд работы с памятью, при помощи замены абсолютных адресов на короткие относительные смещения. А сегменты это по сути псевдонимы каких-то линейных областей памяти, которые по особому используются определенными инструкциями процессора. Ну и естественно с введением страниц для совместимости сегменты оставили. Ну кажется начинаю понимать.
Но по моему в защищенном режиме существует еще хитрая «ядерная» таблица, которая, если правильно помню, называется VAD. Вернее не таблица, а дерево, в котором задаётся соответствие физических страниц и виртуальных страниц из адресного пространства процесса. Я так понял, что это последний шаг для получения адреса, посылаемого контроллеру памяти, из линейного адреса, вычисляемого описанным вами образом, при обращении к сегменту? Наверное тогда значения сегментных регистров совсем не равны 0, специфичны для потока и сохраняются/восстанавливаются при переключении контекста потока, как и остальные? Вот это было бы интересно узнать.
Как я понимаю, сегменты появились изначально, до страниц, как способ сэкономить на размерах операндов команд работы с памятью, при помощи замены абсолютных адресов на короткие относительные смещения. А сегменты это по сути псевдонимы каких-то линейных областей памяти, которые по особому используются определенными инструкциями процессора. Ну и естественно с введением страниц для совместимости сегменты оставили. Ну кажется начинаю понимать.
Но по моему в защищенном режиме существует еще хитрая «ядерная» таблица, которая, если правильно помню, называется VAD. Вернее не таблица, а дерево, в котором задаётся соответствие физических страниц и виртуальных страниц из адресного пространства процесса. Я так понял, что это последний шаг для получения адреса, посылаемого контроллеру памяти, из линейного адреса, вычисляемого описанным вами образом, при обращении к сегменту? Наверное тогда значения сегментных регистров совсем не равны 0, специфичны для потока и сохраняются/восстанавливаются при переключении контекста потока, как и остальные? Вот это было бы интересно узнать.
0
>сегменты появились изначально, до страниц, как способ сэкономить на размерах операндов…
Скорее, на разрядности регистров сэкономили. В 8086 регистры 16-разрядные были, а шина адреса — 20 разрядов, так что без сегментов там никак.
В 386 уже 32-разрядная шина, и регистры такие же ( как раз хватит, чтоб адресовать 4 Гб). Поэтому в сегментах особого смысла нет, и остались они в основном из-за совместимости. В AMD x86-64 их вообще выпилили, в нативном режиме там сегментные регистры недоступны.
>по сути псевдонимы каких-то линейных областей памяти…
В общем, да.
>Но по моему в защищенном режиме существует еще хитрая «ядерная» таблица…
При включенной линейной адресации там получаются два уровня. Сначала адрес сегмента складывается со смещением, получается линейный адрес.
Потом линейный адрес смотрится в трёхуровневом каталоге, который так и называется «Page Directory», оттуда берется физический адрес, и по этому адресу процессор лезет в память. Сам каталог лежит в оперативке, а его физический адрес в регистре CR3.
В каталоге каждой странице тупо проставлен бит — присутствует она в памяти или нет. Этот бит аппаратно проверяется. Если страницы нет в памяти, вызывается исключение Page Fault (INT 0Eh). Операционка его отлавливает и может делать что хочет — например, выключить линейную адресацию, загрузить эту страницу в память по любому физическому адресу, апдейтить физический адрес в каталоге, включить линейную адресацию обратно и сделать вид, что ничего не произошло.
Грубо говоря, любой линейный адрес можно мапить на любой физический. При переключении задачи просто подсовывается новый каталог страниц. Таким образом, один и тот же линейный адрес мапится на разные физические, в зависимости от того, из какого потока идет обращение.
Для прикладной программы это без разницы, она не заметит подмены. В Windows вообще у всех пользовательских процессов один и тот же базовый адрес всегда, и сегментные регистры одинаково настроены. Только на уровне каталог страниц у каждого потока свой, так что физические адреса разные получаются.
Скорее, на разрядности регистров сэкономили. В 8086 регистры 16-разрядные были, а шина адреса — 20 разрядов, так что без сегментов там никак.
В 386 уже 32-разрядная шина, и регистры такие же ( как раз хватит, чтоб адресовать 4 Гб). Поэтому в сегментах особого смысла нет, и остались они в основном из-за совместимости. В AMD x86-64 их вообще выпилили, в нативном режиме там сегментные регистры недоступны.
>по сути псевдонимы каких-то линейных областей памяти…
В общем, да.
>Но по моему в защищенном режиме существует еще хитрая «ядерная» таблица…
При включенной линейной адресации там получаются два уровня. Сначала адрес сегмента складывается со смещением, получается линейный адрес.
Потом линейный адрес смотрится в трёхуровневом каталоге, который так и называется «Page Directory», оттуда берется физический адрес, и по этому адресу процессор лезет в память. Сам каталог лежит в оперативке, а его физический адрес в регистре CR3.
В каталоге каждой странице тупо проставлен бит — присутствует она в памяти или нет. Этот бит аппаратно проверяется. Если страницы нет в памяти, вызывается исключение Page Fault (INT 0Eh). Операционка его отлавливает и может делать что хочет — например, выключить линейную адресацию, загрузить эту страницу в память по любому физическому адресу, апдейтить физический адрес в каталоге, включить линейную адресацию обратно и сделать вид, что ничего не произошло.
Грубо говоря, любой линейный адрес можно мапить на любой физический. При переключении задачи просто подсовывается новый каталог страниц. Таким образом, один и тот же линейный адрес мапится на разные физические, в зависимости от того, из какого потока идет обращение.
Для прикладной программы это без разницы, она не заметит подмены. В Windows вообще у всех пользовательских процессов один и тот же базовый адрес всегда, и сегментные регистры одинаково настроены. Только на уровне каталог страниц у каждого потока свой, так что физические адреса разные получаются.
+1
Вот пример клёвый, как физические адреса можно менять прямо на лету.
www.asm-faq.ru/processory-intel-v-zashhishhennom-rezhime/163-stranichnaja-adresacija.html
www.asm-faq.ru/processory-intel-v-zashhishhennom-rezhime/163-stranichnaja-adresacija.html
+1
И да, при переключении задач (т.е. потоков) сегментные регистры аппаратно сохраняются. Специальное место есть для этого, сегмент состояния задачи (TSS).
+1
Ага, спасибо. Ну теперь я имею представление о том как оно всё устроено.
0
первое впечатление — джордейн чайными ложечками...)
+1
Вы дальнейший код хотите писать на Си или до конца на асме?
0
На Си. Уже через выпуск планируем перейти. Пока по времени не очень получается(
0
Only those users with full accounts are able to leave comments. Log in, please.
Пишем свою ОС: Выпуск 2