Сайт автора мёртв - но страница сохранена

Хотелось бы услышать мнения остальных по статье

Moderator: Shaos
ПО моему автор просто разделяет вещи, есть пользовательская ОСЬ а есть ОСЬ для управления оборудованием. ВОт для этого уже родились вполне дееспособны ОС.Shaos wrote:Судя по всему статья ОЧЕНЬ старая - с тех пор и PMODE/W стал бесплатно входить в OpenWatcom, и свободные реализации биосов появились. Создаётся ощущение, что автор делает неправильные выводы, ища корень зла Windows в многозадачности - искать надо в кривизне ОС как таковой
Видимо он не работал толком с Linux, а также знать не знает про RealTime ОСы...
C линуксом так делают вроде - минимальный биос, нужный только для работы линукса, ну и сам линукс в минимальной конфигурации в той же ПЗУCHRV wrote:А у меня есть лицензионная http://phystechsoft.ru/ptsdos/
Кстати при современных обьемах стартап ПЗУ материнок в комплект к бивису можно разместить батхэд - небольшую ось, ну например тот же ptsdos.
Наеврно уже так делают...
У меня тоже.CHRV wrote:А у меня есть лицензионная http://phystechsoft.ru/ptsdos/
Слышал, тока PXE загрузчик и ROMDOS. Но все же, объемы не так уж и велики... Даже сам БИОС пожат.CHRV wrote:Кстати при современных обьемах стартап ПЗУ материнок в комплект к бивису можно разместить батхэд - небольшую ось, ну например тот же ptsdos.
Наеврно уже так делают...
Поддержим отечественного производителя спутников серии "Ураган"!CHRV wrote:А у меня есть лицензионная http://phystechsoft.ru/ptsdos/
Есть новый стандарт, называется EFI. Extensible Firmware Interface. По сути это - микро-ОС, размещенная в ПЗУ. Поддерживает работу с USB, различными носителями, читает файловые системы, грузит файлы. Массово применяется на данный момент только в интелмаках, возможно еще в IA-64 машинах (хотя я не видел их живьем).CHRV wrote: Кстати при современных обьемах стартап ПЗУ материнок в комплект к бивису можно разместить батхэд - небольшую ось, ну например тот же ptsdos.
Наеврно уже так делают...
Проект 32-битной однозадачной ОС
Прежде всего, данный проект не ставит целью создать конкурента ОС Windows всех видов. Его цель заполнить пустующую нишу DOS подобной однозадачной ОС, но полностью 32 - битной. Одна из причин необходимости такой ОС - желательность для некоего класса задач, запрета страничного преобразования, простоты операций ввода/вывода, экономного расхода памяти самой ОС и выделения всего процессорного времени одной задаче. Подробнее все это расписано на этой же страничке в статье "Отрицательные стороны многозадачности ОС". В большей степени это относится к задачам интенсивных рассчетов, моделирования, управления внешними устройствами сбора и обработки информации. По назначению, это будет скорее инструментальная ОС, т.е. ближе к QNX, но без ее многозадачности, с интерфейсом традиционного ДОС и встроеной оболочкой типа NC/VC.
Ну нельзя же, до сих пор, при необходимости сделать свою простую програмку, делать это в антикварном 16-битном ДОС и упихивать все в несчастные 640К, да еще порциями не более 64К (а поработайте-ка с портами ввода/вывода из под Windows...).
Есть предположение, что не только те, кто решает задачи требующие подобную среду, но и обычные владельцы компьютеров, для многих применений вполне удовольствовались бы однозадачной средой, если под нее существовали бы устраивающие их прикладные программы. Непомерная прожорливость и медлительность Windows и значительная сложность Linux (даже несмотря на попытки сделать ее более дружественной), говорят в пользу подобной точки зрения, Да и то, что DOS+NC гораздо проще Windows и уж тем более Linux может создать определенное предпочтение. Кстати задача работающая с несколькими однотипными окнами отнюдь не требует многозадачную среду (чтоб каждое окно было отдельной задачей), ей лишь желательны динамические структуры данных (см.ниже), чтобы управлять выделением памяти для этих окон.
Вообще идеология данной ОС прямо противоположна общепринятым подходам "облегчить жизнь программисту, а за производительность пусть отдувается компьютер - даром что у него сотни мегагерц и мегабайт". Данная ОС "удобна" именно компьютеру, зато позволяет задействовать его ресурсы по возможному максимуму (хотя есть частные исключения, например вряд ли удастся задействовать аппаратную акселерацию видеокарт, документация по которой практически недоступна, а следовательно невозможно создание драйверов).
Внимание! Проект этот частный, никем не финансируемый, не сулящий никаких коммерческих перспектив и вообще непонятно почему начавший реализовываться. Вероятно, причины побудившие автора к его реализации те же, что побудили вольноопределяющегося Марека стать редактором журнала "Мир животных"...
Оболочку предполагается сделать подобной Norton/Volkov Commander ибо по убеждению автора, более простой, оперативной и достаточно удобной оболочки для работы с файлами, человечество еще не придумало. Встраивание оболочки в ядро системы имеет недостаток - другую более подходящую по своему вкусу оболочку не поставишь (например попытка Microsoft, в свое время, создать альтернативу NC - свой Dos Shell провалилась, и автор не встречал никого, кто ей бы пользовался). Возможно это тоже решаемо. Система предполагается более/менее открытой и переписать при желании оболочку на лучшую - флаг в руки...
Почему - более/менее?
Ну это исходя из опыта создателей V2_OS. Полностью открытый, на стадии разработки, исходный текст привел к тому, что появилось множество измененых вариантов этой ОС, несовместимых друг с другом и неотслеживаемых авторами. Объеденить в единую все новоявленые версии, пусть даже с хорошими изменениями, занимает времени больше чем создание собственно ОС. Поэтому авторы V2_OS оставили тексты открытыми, но засунули их на своем сайте подальше, чтоб найти их было потруднее. Облегчило ли это им жизнь, автору неизвестно. Появление сторонних версий готовой ОС, это нормально при условии их полной совместимости с оригинальной, или приведение к совместимому варианту обеих. Но когда ОС на стадии разрабоки желательно изменения предлагать только в рамках авторской версии.
Голландская V2_OS практически преследует те же цели. Однако хорошего впечаления она пока не произвела, но это возможно из-за ее далеко незавершенности. Ну что ж. Попробуем сделать получше. Возможно на стадии готовности проекта проявятся преимущества и недостатки обеих систем. Создатели V2_OS нажимают больше на ее мультимедийное и игровое применение, а данная ОС больше инструментальная.
Исполняемые файлы скорее будут напоминать COM, чем к EXE. Предполагается совпадающая модель памяти для кодов и данных в пределах выделеного задаче логического сегмента (но не от 0 до 4G). Стеку выделяется отдельный сегмент не совпадающий с CS/DS. Заголовок вероятно так же будет отсуствовать и старт задачи происходить с логического 0. Reallocations заголовка EXE файлов не предполагаются вообще. Нужда в них возникает крайне редко и адрес в физической (или линейной) памяти можно будет получить альтернативными способами (для этого можно предусмотреть системную функцию). При необходимости, получить доступ ко всем 4G памяти можно будет через системные дескрипторы, правда только в этой однозадачной ОС. В случае ориентировки задачи и на многозадачную ОС это не пройдет, системные дескрипторы там должны быть недоступны. Работать прикладная задача будет с высшим уровнем привилегий. Нужды в защите "чужих" задач в однозадачке нет, а вызовы системных функций не будут требовать переключения задачи для смены уровня привилегий, что гораздо быстрее. Это выйдет "боком" в случае моногозадачки, где эта же задача на системных вызовах "утонет" в переключениях.
Название скорее всего останется OSKA т.е в уменьшительном русском произношении "маленькая операционная система". Какого-либо аббревиатурного содержания во второй половине названия нет. Наиболее логичное DOS32 вероятно закопирайчено вездесущим Microsoft для совершенно сторонней этим целям программы какого-то своего backup.
Разработка ведется на нескольких Ассемблерах из-под Dos.
Требование к процессору - минимально 586 семейства. Основная причина - использование переадресовки прерываний в V86 режиме. Для их задействования требуется Intel Pentium, AMD K5 и т.п. но их нет у Cyrix
В создание языков высокого уровня, предлагается всем желающим попробовать свои силы на этом поприще. Автор этим точно заниматься не будет, хотя если таковых не найдется, систему можно уже сейчас считать мертворожденной. Впрочем существует например FREE Pascal в исходных текстах. Могут найтись C и другие языки. Положим сделать сразу хороший оптимизирующий, а то и объектно-ориентированый компилятор чересчур непросто, но пусть для начала появится хоть какой, лишь бы ошибок делал поменьше. Писать уже можно будет. Кстати, исходники Watcom C говорят открыли.
Автор не сторонник объекто-ориентированого програмирования. Оно не слишком подходит для счетных задач. Это вообще очень затратный подход к программированию, чем кстати перегружены продукты Microsoft. Хотя для некритичных ко времени исполнения фрагментов, кому нравится - пусть использует.
Подход высокоуровневых компиляторов к работе с файлами так же, предполагается к реализации не совсем традиционный. Раньше ОС создавала небольшие файловые буферы, через которые и осуществлялся обмен мелкими порциями, причиной чему были, небольшой размер памяти и невозможность прямой адресации данных превышающих 64К. Сейчас предполагается обмен файла исключительно напрямую с буфером задачи, минуя буферизацию ОС. (напимер такие операции Pascal как readln / writeln должны идти через собственную буферизацию организуемую компилятором который скорее всего просто будет считывать весь файл в память, а затем изображать над ним работу по записям. Маловероятно что вам попадется текстовый файл способный не влезть в память даже недорогой машины). Пока, полностью, идеология работы с файлами не вырисовалась.
Кстати, работа со структурами данных предполагается более ориентированой на дескрипторы, когда любой структуре может быть выделен свой дескриптор. Логический адрес ее начинается с 0, а размер начинает контроллироваться пределом (правда с шагом 4К). Смысл это имеет только для структур значительного размера. Динамические структуры будут доступны только через свои дескрипторы. Это имеет и недостаток. Свободных селекторов всего два (GS, FS) и слишком часто тасовать дескрипторы плохо, ведь они подгружаются из памяти, а это не есть быстро.
Ситуация с системными утилитами, и минимально необходимым набором прикладных программ. По возможности, автор будет их делать сам, но помощь не помешает. В одиночку - не хватит никакого времени. А без них система тоже мертва. Впрочем, этим можно заниматься только после готовности системы хоть на минимальном уровне. На стадии проекта это не актуально.
То же можно сказать про драйвера не входящие в минимальный набор, и про поддержку сторонних файловых систем.
Вообще писать собственно ОС - самое неблагодарное занятие. Практика показывает - материально что либо поиметь вероятно только с прикладных программ, тем более у нас, где платить за программы считается признаком очень дурного воспитания, а то и вообще - законченого идиотизма. Да и Linux тенденции - ОС должна быть бесплатной, находят горячий отклик в сердцах потенциальных халявщиков. Однако, например хороший текстовый процессор (с масштабируемыми (своими, а не лицензионными TTF) шрифтами, вставкой таблиц, но без всеохватной раздутости Word) мог бы иметь определенный успех, при близкой к символической цене "левого" лазерного диска.
Файловая система предполагается своя, с преимущественно нефрагментированым хранением файлов. Кстати, ФС V2_OS предполагается подобной. Чисто нефрагментированное хранение файлов имела RT-11 для компьютеров PDP. Доступ к файлам подобная система обеспечивает более простой и быстрый, однако быстро расходуется нефрагментированная свободная память и требуется последующая дефрагментация. Может сложиться ситуация, когда при попытке сохранить результат работы, вы получите сообщение, что нет ни одного свободного фрагмента достаточной длины, хотя свободной фрагментированой памяти будет предостаточно. Неясно, что в этом случае сделают создатели V2_OS, но данная ФС предполагает сохранение в таком случае файла фрагментированым, по принципу - предыдущий фрагмент указывает на последующий (принцип известный давно, но кажется никогда не реализовывавшийся). Впоследствии можно провести дефрагментацию, т.к. работа с фрагментироваными файлами резко замедлится.
Вместо FAT использоваться будет его аналог Linear File Allocation Table (LFAT), который будет хранить информацию о свободных и занятых фрагментах, их стартовом адресе и размере.
Для большей устойчивости ФС предполагается в конце каждого файла (или его фрагмента) один сектор выделять на полный описатель файла, (включая его положение в дереве каталогов). Таким образом, даже при полном уничожении ФС, она будет практически полностью восстановима (опыт показывает что полностью данные на диске затираются редко, чаще всего страдают FAT или DIR). Это так же допускает возможность хранить LFAT в оперативной памяти, не переписывая ее на диск при записи каждого файла. Предполагается привлечь к хранению информации о незавершеной последней записи CMOS, и при сбое питания, при перезапуске система сможет из описателей файлов восстановить LFAT (что-то вроде автоматического запуска DiskScan, при незавершеном выключении Windows). Каждый записываемый описатель будет хранить ссылку на предыдущий, что при сбое питания позволит восстановить всю цепочку записаных файлов и скоректировать LFAT и DIR. LFAT предполагается размером 128К, таким же как и FAT16, что позволит без проблем кешировать и ФС FAT16. Размер в 128K позволит хранить в LFAT до 16К фрагментов, что в зависимости от распределения составит 8 - 16К файлов на раздел (а если хранить только размеры, то эта цифра удваивается). Предполагается что если у пользоваеля большой диск, то и часть файлов там тоже будут большими а для очень большого количества маленьких лучше сделать больше разделов. Хранение дерева каталогов пока вообще не определено, хотя текущий каталог (DIR) предполагается хранить в памяти, подобно LFAT, Собственно в памяти предполагается хранить два LFAT и два текущих DIR, по числу окон оболочки, что при переписи файлов позволит получить их полное кеширование. Работа задачи с большим числом разделов будет приводить к перезагрузке кешей. Вообще, для хард дисков, предполагается исключительно "щадящий" по сравнению с традиционными ФС режим работы. А когда позиционирования головок меньше - и работа быстрее. Скорость работы собственной ФС должна превзойти все остальные, правда при условии, пока файлы не фрагментированы.
Размер встроеных кешей будет определять поддержку только собственной ФС и FAT16. Ну, разумеется флопик и желательно CDFS. Поддержка других возможна подзагрузкой соответствующих модулей, со своим кешированием.
Обойтись без собственной ФС - нет подходящей кандидатуры. FAT16 для размеров современных дисков себя исчерпала, да и расходует дисковое пространство неэкономично. FAT32 экономичнее, но гораздо медленнее и может иметь гиганский FAT, что может быть нежелательно для задач которые требуют много памяти (виртуальная память в данной системе вообще не предусматривается). Предположим у вас диск 10G. Кластеров по 4К выходит больше 2,5М, да каждый элемент FAT 4 байта, вот вам 10М на один кеш, а кешей два...
Использовать ФС сетевых ОС - NTFS или от Linux? Они громоздки, там много ненужной в персональном компьютере информации о правах доступа и т.п., ведь они предназначены для многопользовательских систем.
Чисто психологически автору хотелось бы упихать всю системную часть в 640К первого мегабайта, чтоб прикладная задача размещалась начиная с первого мегабайта и судя по всему базовый вариант будет именно таков.
В стандартные дисковые драйвера войдут только IDE PCI Bus Master UDMA или DMA). SCSI если кто захочет, пусть делает сам, но будучи сделаным - можно его включить в систему как стандартный. Доступ к FLOPPY оставлен через BIOS 16-битным.
Видеокарта системой используется в текстовых режимах и уже сделана графическая эмуляция текста. Драйвера вероятны в пределах VBE. Остается стандартизировать функции. В целом это не трудно (включил видеорежим/выключил), но к сожалению неподдерживаемость производителями стандарта акселерации (VBE/AF) делает возможным ее использование только в случаях доставания правдами и неправдами описаний аппаратных регистров акселерации по каждой конкретной видеокарте и создания персональных драйверов. Информация эта у разработчиков довольно дорогостоящая, но вызовы все равно надо закладывать в проект.
Поддержка 16-битных драйверов (за исключением функций BIOS) и задач Real режима, через организацию виртуальной 16-битной машины, не планируется. Это потребовало бы сдублировать все функции DOS, а их предполагается наоборот - свести к возможному минимуму, и безо всякой совместимости.
Сперва создается загрузчик системы из под ДОС, с возможностью возврата. Это будет что-то вроде разновидности ДОС экстендера ориентированого на модель памяти и формат задачи новой ОС. В отличие от традиционных ДОС экстендеров, тут будет полный захват памяти и полное отсуствие ДОС поддержки, а все системные сервисы будут свои. Стандартными будут только функции BIOS. Это позволит уже на ранних этапах создавать прикладные задачи, правда ДОС компиляторами (все равно других пока нет).
Автор не претендует на лучшее знание всех сторон создания ОС. Ни одно из вышеприведеных положений не является догмой и готово к изменению в лучшую сторону.
Предлагается всем заинтересовавшимся этим проектом принять участие в обсуждении и выработке варианта годного к реализации.
Англоязычное общение возможно, хотя и не особо желательно, ибо по английски автор говорит примерно как Черномырдин по русски, т.е. много и охотно, однако понять сказаное до сих пор никому так и не удалось.
Данное описание набрасывает эскиз системы в общих чертах и будет крайне редкообновляемым.
Колесницкий В.М.
E-mail: kolesnitsky@caucasus.net
Алексей Вольский (ayvo)
E-mail: ayvo@online.nsk.su
НЕСКОЛЬКО СЛОВ В ЗАЩИТУ МНОГОЗАДАЧНОСТИ
На самом деле, в Вашей статье практически ничего не сказано собственно про мультизадачность. Все претензии относятся к ее _конкретным реализациям_ на персональных компьютерах.
Собственно говоря, основных претензий две - страничный режим и защита по привилегиям. О них чуть ниже. Но в Вашей статье даже не упомянуты самые главные, на мой - и не только мой - взгляд, недостатки этих OS:
а) они ОЧЕНЬ ОЧЕНЬ БОЛЬШИЕ,
б) они очень плохо сделаны.
На среднем писюке под win95 только уровень vmm/vxd съедает порядка полутора мегабайт памяти. Добавим сюда дисковый кэш, совершенно бессмысленный в системе с виртуальной памятью. Добавим сюда просто ужасный код системных dll (посмотрите книжки Мэта Питрека "Внутренний мир Windows" и "Секреты системного программирования в Windows95). Для пользовательских программ просто не остается памяти.
Общим местом является то, что программы от MS имеют раздутый (bloat) интерфейс с множеством функций, которые подавляющим большинством пользователей никогда не используются. Но ведь то же самое можно сказать и об api win32 (и о сервисах vxd, если уж на то пошло). Фактически, никакого api нет. Есть множество разнородных функций, написанных "на всякий случай", многие из которых вообще ничего не делают.
Вообще говоря, помимо низкой квалификации большинства программистов, работавших над кодом windows, создается впечатление, что фирма microsoft страдает настоящим комплексом неполноценности. Многие вещи в windows обязаны своим появлением навязчивому желанию, "чтоб все было как у людей". Например, posix подсистема в NT, которую, насколько мне известно, не использует НИ ОДНА программа в мире.
На самом деле, конечно, этот комплекс во многом основан на чисто маретинговых соображениях. "В windows должен быть дисковый кэш, а то конкуренты убедят пользователей, что windows плохая. Исполняйте." И программисты исполняют. "Windows (по крайней мере, на словах) должна быть надежной. Плохие программы не должны заниматься вредительством." o'kay, накручиваем ужасающий bloat проверки параметров на каждую функцию api. "Но windows должна быть эффективной". o'kay, разрешаем kernel'у лазить в системные структуры кольца 0 (а значит и всем остальным тоже). И так далее, и так далее.
Так что низкая производительность windows программ очень в малой степени зависит от таких "мелочей", как страничное преобразование. Когда переключение цепочки занимает почти миллисекунду, поневоле разочаруешься в многозадачности вообще.
А как же Linux ? Здесь ситуация, кончено, лучше. Юниксойды и архитектуру имеют более разумную и написаны в основном не за зарплату. Но здесь вступает в игру другой фактор. Все юниксы - не персональные OS. Они с самого начала были ориентированы на МНОГОПОЛЬЗОВАТЕЛЬСКИЕ системы. Отсюда и определенные требования к защите (нельзя позволять игроку в неотлаженный тетрис завешивать почтовый сервер...). Отсюда необходимость в сложной файловой системе с приоритетами, правами и прочим. Кстати, стремление быть "похожими на unix" принесло все эти вещи и в мир wintel. Но следующие отсюда несуразицы несущественны по сравнению с бюрократической реализацией виндовоза.
Кроме того, в юнике тоже достаточно bloat. Гляньте хотя бы на код gcc. Это очень хороший пример - как не надо писать программы. (А вообще лучший компилятор всех времен и народов - это object pascal 32 из delphi... нет, я серьезно, посмотрите под дизассемблером, какой код он генерирует, и сравните с любым другим компилем
Но вернемся к unix. Наглядным примером bloat, вытекающим из многопользовательского характера unix, является X. Я не хочу сказать ничего плохого про ее авторов. X написана хорошо, так, как только и можно написать графическую подсистему для распределенной многопользовательской среды. Но перенос этого кода на персоналку ... ну, Вы меня понимаете.
Ну и на закуску. Unix написана очень давно. Хотя за прошедшие годы его неоднократно клепали, общая идеология осталась прежней. И это тоже дает о себе знать...
Короче говоря, на сегодняшний день нет достаточно распространенной os для персональных компьютеров, которая была бы тем, чем она должна быть.
Остаются, правда, еще некоторые штучки, вроде QNX. Судя по доступной информации, - это отличная os для i386up. Хотя потенциально она может обслуживать и распределенную среду, в силу настоящей микро-ядерной структуры и отличной модульности, вариант для одного пользователя с gui, текстовым редактором, браузером и проч. влазит на 1.44 дискету. Да, я понимаю, что это рекламный трюк. На самом деле, чтобы стать полноценной os, этому демо-варианту нужна, по крайней мере, поддержка какой-нибудь файловой системы и кое-какого железа (например, hdd). Но все равно, красиво. Беда только в том, что QNX - чисто коммерческая штучка, и за свои услуги - как всякая конфетка - берет дорогоИ спереть ее (в отличие от windows) негде.
А все экспериментальные и исследовательские os вроде mach, vsta, L4 (это, собственно, микро-ядра) и проч. (весьма исчерпывающий список есть на cs.arizona.edu) опять-таки ориентированы на всяческие распределенные и супер-компьютерные применения. Вот такие дела.
Да, я еще обещал обсудить страничное преобразование и защиту. Увы мне. Придется выполнять. Итак, для чего в общедоступных os используется пагинация ?
* 1) для разделения адресных пространств отдельных задач (то бишь для защиты),
* 2) для связи между задачами (можно отразить что хочешь куда хочешь),
* 3) для увеличения объема доступной памяти.
Первые два пункта НЕ НУЖНЫ в персональной os. Этих же целей можно добиться более дешевыми средствами. По крайней мере, на i386up для этого вполне годятся селекторы. Защита на уровне селекторов очень устойчива против программных ошибок (при условии если обычной программе нет нужды часто загружать сегментные регистры), но бесполезна против злоумышленного вредительства (знаючи, например, селектор данных ядра всегда можно сделать какую-нибудь пакость). Страничное преобразование обеспечивает более надежную защиту. В cr3 пользовательская программа записать не может, а в кольцо 0 ей, теоретически, залезть не дадут (в обеих windows, конечно же, есть несколько дыр, как же без этого. Самая простая - использовать сервис dpmi для создания шлюза). Но в однопользовательской системе и незачем защищаться от вредителей ! Откуда им там взяться ? Разве-что вирус ... Но на то антивирусы и существуют.
Более серьезным соображением является увеличение объема памяти. Эта штука полезна на любой машине. Фактически, большинство нормально написанных windows прог сегодня вообще не проверяют out of memory error. Это здорово.
Но тут есть два момента. Во-первых, пагинация не решает проблему памяти кардинальным образом. Фактически, во всех известных мне os оверсвоп появляется уже при размере виртуальной памяти лишь вдвое превышающем размер физической. Собственно wintel системы без оверсвопа - это почти что исключение из правила. Так что все равно пямятишку бы неплохо докупить ... По нынешним временам, кстати, это весьма дешевая вешь.
А во-вторых, использование pagefile для увеличения объема памяти - это весьма и весьма опциональная вещь. По сути дела, работа ни одной из ключевых систем os не зависит от ее наличия (драйвер диска, конечно, должен страховаться, но и только). Мы, конечно, предполагаем, что пагер грузиться последним и виртуализует адреса только для пользовательских программ. Системные штуки-дрюки все фиксированы в памяти. Но они и так фиксированы во всех os с пагинацией (А как иначе ? А если кто-то перехватит прерывание ?) Windows, правда, теоретически разрешает vxd участвовать в свопе, но все vxd, которые действительно относятся к операционной системе (а не просто болтаются в кольце 0, как vwin32), сами себя фиксируют.
Итак, пагинация в персональной os - это просто один из возможных сервисов. Вроде дискового кэша в dos. Хочешь - включил pagefile.com в config.sys, хочешь - заремировал. 8(
Кстати, Вы где-то там сетуете, что менеджеры памяти не используют страниц по 4 мега (а есть еще ведь и 2-х меговые). Но такие страницы совершенно бессмысленны для подкачки. Их основное предназначение - фиксированные страницы (ядро, видеобуфер etc.) Идея заключается в том, чтобы уменьшить число TLB промахов. И, кстати, виндовоз это делает. Правда, довольно бестолково - перезагружая постоянно CR3 (в соседних кусках кода, вероятно написанных другим программистом в другое время).
Ну и пара слов о защите. Собственно, я уже все сказал выше, когда обсуждал защитные функции страничного преобразования. В персональной os нужна лишь защита от программных ошибок, которая эффективно выполняется при помощи разграничения доступа на основе селекторов. Так что все программы могут запросто выполняться в кольце 0.
Есть, правда, пара тонких моментов, связанных с обработкой некоторых искючений процессора. Все дело в стеке. Ошибку переполнения стека нельзя обработать без переключения стека. То же относиться к двойной ошибке и (oh, yeah) к INT OE (чито будет если инструкция PUSH обратиться к неприсутствующей странице? П..ц будет).
А самый простой способ добиться того, чтобы процессор переключил стек - смена привелегий. Тогда процессор возьмет новый стек из того, на что указывает TR (мультизадачные прибамбасы i386 можно и не использовать, а просто иметь fake tss, как это сделано в том же исходнике Tran'а. Кстати, известные мне модели 386-486 не проверяют предел tss до фактического переключения задачи, так что его размер можно и слегка подсократить). Но если все программы работают в кольце 0, то этот метод не сработает. При обработке прерываний уровень привелегий должен повышаться, а куда дальше то ?
Но это затруднение можно обойти, используя обработку нарушения стека в отдельной tss. Так как можно предполагать, что это исключение будет возникать достаточно редко, задумчивость intel-овских гипопотамов на этой операции не будет играть никакой роли. Двойная ошибка тоже бывает редко (да я уж надеюсь !). Пагинатор, правда, вызывается достаточно часто, но, во-первых, как мы уже определили выше, он опционален, если надо заняться чем-то серьезным (типа brute force attack на пароль вашего шефа, его можно и вырубить. А во-вторых, Int 0E обычно кончается вызовом драйвера диска для чтения и, возможно, записи страницы, так что медлительность переключки (около микросекунды, см. у Григорьева) опять-таки никакой роли не играет.
Упф, рука бойцов колоть устала ! А ведь это только присказка, сказка будет впереди.
Архитектура операционной системы E-Lite
Микро-ядро
Элит - мультизадачная микроядерная система. Это означает, что одновременно выполняется несколько отдельных программ. При этом все системные сервисы, такие как драйверы аппаратуры, файловая система и т.д., представлены как отдельные задачи. Микро-ядро Элит обеспечивает взаимодействие между задачами и само реализовано как отдельная задача kern.
В каждый момент времени задача может находится в одном из следующих состояний:
* ts_init - задача создана, но еще не работает
* ts_exec - задача готова к выполнению
* ts_recv - задача ожидает сообщение
* ts_send - задача ожидает ответа на сообщение
* ts_dead - задача уже не работает, но запись о ней еще
не удалена (foot text="В Unix это называется zombie state")
Приоритеты
Каждая задача в системе имеет свой приоритет - уникальное число в диапазоне от нуля до максимального числа задач в системе минус один (по умолчанию в Элит одновременно может существовать 64 задачи, однако, их число может быть увеличено до 256).
В каждый момент времени выполняется задача, имеющая максимальный приоритет (то есть наименьшее значение приоритетного числа). Все остальные задачи, потенциально готовые к выполнению, будут ожидать пока текущая задача не отдаст процессор. Однако, более приоритетная задача может отобрать управление у текущей в результате прерывания.
Таким образом, Элит хорошо подходит для приложений реального времени (учитывая, что Элит предназначена для персональных компьютеров, это в основном игрушки и мультимедиа). Если задача имеет высокий приоритет, отобрать у нее процессор смогут лишь несколько еще более приоритеных системных сервисов, активацию которых задача может проконтролировать (например, драйвер ввода-вывода активируется только если задача к нему обратится).
Приоритет, который имеет задача, определяется двумя параметрами - типом задачи (задаваемом в управляющем слове программы PCW) и порядком запуска задач. Всего существует четыре типа задач:
* pri_serv - системный сервис, наивысший приоритет
* pri_real - задача реального времени
* pri_user - пользовательская задача
* pri_idle - пассивная задача, активируется только когда нет других более приоритетных задач, низший приоритет
Когда задача запускается на выполнение, она получает первый свободный приоритет данного класса. По умолчанию число доступных приоритетов каждого класса таково:
* pri_serv - 20 (0-19)
* pri_real - 1 (20)
* pri_user - 42 (21-62)
* pri_idle - 1 (63)
Однако, эти диапазоны могут быть переконфигурированы. Например, если используемые программы реального времени не очень-то "реальны" и фактически их можно запускать на пару, пользователь может увеличить диапазон ts_real. Обратно, если в системе меньше 20 системных сервисов, их диапаон можно сузить.
Заметим также, что далеко не все системные сервисы требуют высокого приоритета. Например, модуль tcp/ip или файловая система могут (и должны быть) реализованы с приоритетом pri_user. Поскольку такие задачи стартуют раньше, чем обычные пользовательские задачи, они будут иметь достаточный приоритет. Класс pri_serv требуется в основном лишь драйверам, непосредственно работающим с физическими устройствами компьютера (где имеется жесткая зависимость по времени).
Защита
Механизм защиты, встроенный в Элит, ориентирован на персональные - однопользовательские - системы. Используемые средства позволяют предохранить систему от последствий ошибок в пользовательских программах, но не от злонамеренных действий.
Основным средством защиты являются адресные пространства (см. ниже), раздельные для всех задач. Однако, этот механизм реализован не на страничной модели памяти, а на использовании сегментов. Нормальная программа никогда не изменяет сегментных регистров, поэтому никакая ошибка в ней не может привести к записи в чужое адресное пространство (вероятность случайной передачи управления на данные, которые могут быть проинтерпретированы как что-нибудь вроде "pop ds", практически нулевая). Однако, при желании задача _может_ загрузить в любой из сегментных регистров любое значение.
То же самое касается и изменения различных системных ресурсов. Поскольку все задачи выполняются в кольце 0 (защита по привелегиям x86 не используется), любая программа может выполнять инструкции типа "lidt", "mov cr0,0" или записать пару байт в порт 20h ... Но обычная программа этого не делает, и поэтому, не может сделать этого и по ошибке.
Таким образом, в Элит не приходится расплачиваться за надежность существенным ухудшением производительности, как в других "персональных" операционных системах для x86. С другой стороны, такой подход к защите не позволяет применять Элит для многопользовательских систем, где необходимо обеспечивать защиту от "вредительских" программ.
Адресные пространства
Каждая задача выполняется в отдельном адресном пространстве памяти и не видит пространства других задач. Пространства памяти реализваны на основе сегментной модели процессора x86 - каждая задача имеет один селектор данных, загружаемый в регистры DS, ES и SS, и один селектор кода, загружаемый, естественно, в регистр CS. Линейныйе адреса и пределы всех этих сегментов совпадают.
Таким образом, каждая задача владеет непрерывным участком памяти, внутри которого она может делать все что захочет - выполнять данные как код, вносить исправления в свой код, располагать стек в любом удобном месте и т.п. Все это никак не может отразиться на остальных задачах в системе.
Исходный размер адресного пространства задачи определяется старшими 20 битами слова управления программы, которые задают число страниц памяти по 4k каждая, которые необходимо выделить программе. В младшие адреса этого пространства копируется образ программы из файла, а указатель стека устанавливается на конец пространства. В дальнейшем программа может свободно изменить как расположение отдельных своих частей внутри адресного пространства, так и изменить размер выделенного ей блока памяти. Последнее выполняется при помощи посылки соответствующих сообщений микро-ядру.
Надо учитывать, что хотя каждой задаче соответствует какое-то адресное пространство, далеко не всякое пространство содержит в себе задачу. Адресные пространства можно создавать при помощи посылки соответсвующих сообщений микро-ядру. Таким способом, например, резервируется память видеоадаптера и т.п.
Программы
В связи с разделением адресных пространств видимая каждой задачей память всегда начинается по адресу 0. Это позволяет максимально упростить внутренний формат программного файла. Фактически, он эквивалентен тому, что в dos называлось com-файлом.
Вся внутренняя структура состоит из управляющего слова программы (PCW). {foot text = "На самом деле, это двойное слово в терминологии интеловских бегемотов. Но на любом другом процессоре базовая единица памяти называется словом."} Это слово определяет основные характеристики программы и имеет следующий формат:
* начальный размер адресного пространства в страницах (20 бит),
* класс приоритета (2 бита),
* может ли адресное пространство участвовать в свопинге (1 бит),
* должны ли линейные адреса соответствовать физическим (1 бит),
* зарезервировано (8 бит).
Размер адресного пространства, как уже говорилось, затем может меняться программой по мере необходимости. Точно также, программа в дальнейшем может разрешить своп для некоторых своих страниц, послав соответсвующее сообщение менеджеру страниц (если он тут есть).
Взаимодействие между задачами
Для обеспечения связи между задачами микро-ядро предоставляет несколько примитивов, реализующих обмен сообщениями. Для посылки сообщения задача может использовать два варианта:
* post() - асинхронная посылка, и
* send() - посылка с ожиданием ответа.
Во втором случае задача переходит в состояние ts_send и не будет выполняться до тех пор, пока получатель сообщения не вызовет reply(). В случае же post() задача- получатель сообщения активируется только если ее приоритет выше, чем у текущей. В любом случае при использовании post() задача-отправитель остается в состоянии ts_exec и будет выполняться, как только не будет более приоритетных задач.
Вариант с send() больше подходит для ситуаций, когда одна задача запрашивает обслуживание у другой (модель клиент- сервер). По такому принципу реализован ввод-вывод. Когда задача хочет что-нибудь записать на диск, она посылает соответствуюий запрос драйверу жесткого диска, который в свою очередь, выполнив операцию, информрует клиента о результатах при помощи reply().
Алгоритм post() больше подходит для информирования задачи о некотором событии, которого может и не быть. Например, клавиатурные сообщения пересылаются в текщую задачу, владеющую фокусом ввода, именно при помощи post().
Системные сервисы в основном должны пользоваться post(), когда они взаимодействуют с менее приоритетными задачами. В противном случае, пользовательская задача, "не читающая почту" может заблокировать системный сервис в состоянии ts_send на неопределенное время.
Чтобы получить сообщение, задача должна сделать вызов recv(). Если имеется подходящее сообщение, оно будет возвращено и задача продолжит выполнение. Если же такого сообщения нет, задача перейдет в состояние ts_recv, в котором и будет пребывать до тех пор, пока кто-нибудь не пришлет ей сообщение, соответсвующее маске запроса.
После обработки сообщения задача должна вызвать reply().
Если соответствующее сообщение было послано при помощи send(), этот вызов разблокирует отправителя (переведет его в состояние ts_exec) и передаст ему результаты обработки. Если же отправитель использовал post(), вызов reply() не делает ничего. Теоретически, задача может не отвечать на сообщение, если она абсолютно уверена в том, что это сообщение послано асинхронно. Однако, по соображениям надежности всегда лучше вызывать reply().
Заметим, что механизм сообщений представляет собой единственное средство синхронизации между задачами. Это снимает все проблемы, связанные с разделяемыми ресурсами, взаимной блокировкой и т.д. Всякий разделяемый ресурс в системе оформляется как отдельная задача и обращения к нему буферизуются при помощи send() или post().
Чтобы послать сообщение другой задаче, необходимо знать ее идентификатор. Для некоторых основных задач, которые всегда есть в системе, идентификаторы известны заранее. Другие назначаются динамически. Для установления связи между такими задачами используется механизм публикации. Существует специальная задача - сервер имен (это самая заурядная задача класса pri_user). Задачи, желающие предоставить в общее пользование какой-либо сервис, регистрируют себя под каким- либо символьным именем, а клиенты по этому имени узнают идентификатор сервера. Вот как, например, можно пристегнуть файловую систему к драйверу CD-ROM.
atapi-cd /name=teac36 ;говорим CD зарегистрироваться как teac36
cdfsd /cd=teac36 /name=cdrom ;говрим FS использовать этот драйвер
Прерывания
Другим механизмом изменения состояния задачи являются прерывания. Они используются для синхронизации с внешними (обычно аппаратными) событиями. Важно учитывать, что функция обработки прерывания активируется в контексте текущей задачи, а не той, которая установила этот обработчик прерывания.
Такой подход позволяет упростить схему диспетчеризации прерываний, а значит - уменьшить время реакции на прерывание. Типичный обработчик прерывания должен выполнить все действия, критичные по времени, а затем - если нужно - активировать свою собственную задачу.
Для синхронизации задачи с установленными ей обработчиками прерываний используются те же самые post()/recv(). То есть обработчик прерывания от имени текущей задачи посылает сообщение своей. Если последняя имеет более высокий приоритет, текущая задача отдаст процессор до лучших времен.
А как обработчик может найти свою задачу ? По собственному селектору кода. Посколько селекторы кода и данных для каждого адресного пространства имеют одинаковые линейные адреса, обработчик может прочитать всю нужную ему информацию (например, идентификатор своей задачи, сохраненный в глобальной переменной), используя префикс замены сегмента CS.
Режим разделения времени
Описанная выше жесткая схема приоритетов хорошо подходит для планирования системных и критичных по времени приложений. Однако, она не очень удобна для большинства диалоговых программ. Допустим, пользователь последовательно запускает программы А и Б, после чего переключает фокус ввода на А. Теперь он уже не сможет переключиться обратно к Б до тех пор, пока А не перейдет в состояние, отличное от ts_exec, так как у А приоритет выше.
На самом деле, так как любая диалоговая пограмма постоянно запрашивает обслуживание у других задач, и большую часть времени ожидает пользовательского ввода, в большинстве случаев эта блокировка не заметна для пользователя. Но если в нашем примере программа А займется чем-нибудь вроде вычисления благоприятного для женитьбы расположения звезд в римановском пространстве произвольной размерности, Б окажется блокированной...
Данную проблему можно решить на уровне самой программы. В данном примере программа А (астролог) могла бы создать новую задачу с и поручить ей всю грязную работу (ниже всех pri_user задач), а сама вернуться к диалогу с пользователем. Однако, отслеживание в программе всех мест, где теоретически может возникнуть длительная блокировка, достаточно нетривиальное занятие. В любом случае операционная система не может полагаться на пользовательские программы в таком важном вопросе...
Поэтому требуется некий механизм смены одной задачи другой по тайм-ауту. Этот механизм обеспечивается менеджером задач. Он представляет собой просто обработчик прерывания от таймера, запрограммированного на генерацию периодических (допустим, 20 раз в секунду) прерываний.
Каждый раз, когда обработчик активируется, он проверяет - имеет ли текущая задача класс приоритета pri_user. Если да, он посылает создаваемой при старте системы задаче idle (с самым низким приоритетом из возможных) {foot text="Такая задача все равно нужна при используемом механизме планировки"} какое-нибудь сообщение при помощи send(), снимая таким образом текущую задачу с процессора и передавая управление следующей активной.
Когда больше не останется активных задач, выполняться начнет idle (больше некому). Его действие по умолчанию заключается в чтении всей приходящей ему почты и вызове reply(), сообщающем об ошибке {foot text="Это опять-таки надо, чтобы майл-бокс idle не засорялся возможными ошибочными посылками. У других программ такой проблемы нет, т.к. они выполняют какие-то функции и, следовательно, читают свою почту осмысленно."}.
Но ответ на сообщение, посланное менеджером задач, приведет к активации той задачи, от имени которой это сообщение посылалось. И все пойдет по кругу. Задача работает, снимается с процессора, активируется idle, читает очередное сообщение, активируется следующая задача, работает, засыпает и т.д., и т.д. Таким образом, происходит циклическая смена пользовательских задач. Заметим, что поскольку менеджер задач переводит текущую задачу в состояние ts_send, ротация не нарушает базового принципа - всегда выполняется задача с наивысшим приоритетом _из числа готовых_ к выполнению.
Приложения, критичные к времени выполнения
Существует небольщой класс задач, которые не могут выполняться в режиме разделения времени (это игрушки, обработка мультимедиа, а также некоторые "долгоиграющие" математические моделяторы). Обычно подобные задачи также предъявляют и повышенные требования к использованию системных ресурсов.
В Элит они относятся к классу pri_real. Так как их приоритет выше, чем у пользовательских задач, они будут выполняться немедленно, как только будут готовы к этому. Прервать их может только какой-нибудь системный сервис. Но системные сервисы не активируются без причины. Если нет прерываний от аппаратуры и нет запросов на обслуживание, они мирно спят (ts_recv). А поскольку текущая задача имеет статус ts_real, она может быть уверена, что никто кроме нее не вызовет системный сервис (одновременный запуск нескольких риалтаймовских приложений оставим на совести пользователя, если его все устраивает - почему нет ? "Ну, дергается маленько экран...")
Фактически, задача класса ts_real может полностью монополизировать компьютер (железо + системные сервисы). Например, так:
struct mail msg;
msg.dest = kern;
msg.link = kern_memmgr;
msg.what = kmm_realloc; //изменить address space текущей задачи
msg.data = 0;
msg.info = 0xFFFFFFFF; //вся доступная память
if(send(msg) == e_fault) exit(1);
msg.dest = pagefile;
msg.link = page_ioctl;
msg.what = pg_disable; //предполагается, что в PCW программы
msg.data = 0; //установлены биты cw_fixed и cw_mirror
msg.info = 0;
if(send(msg) == e_fault) exit(1);
msg.dest = kern;
msg.link = kern_irqmgr;
msg.what = irq_disable;
msg.data = 0;
msg.info = 0xFFFF; //все 16 каналов
if(send(msg) == e_fault) exit(1);
Вуаля! Мы владеем всей доступной памятью в системе. Своппинг отключен. Прерывания закрыты. Мы их будем открывать по мере необходимости. Пользователь может смело кидать валенок на пульт. Все под контролем. Естественно, подобного рода программа берет на себя всю ответственность за то, что будет дальше. На некоторых чипсетах, например, запрет канала 2 PIC завешивает регенерацию памяти (они думают, что поскольку в стандартном AT это каскад, его можно использовать для своих нужд). Но если так надо, мы _можем_ это сделать. Элит этому не помешает. Важно лишь, что _по ошибке_ подобный код выполниться не может. А если программа делает такие штуки сознательно - флаг ей в руки ...
Скорость реакции системы
Механизм send()/recv() является базой всех микроядерных os. Однако, несмотря на его простоту и логичность, во всех известных ядрах, портированных на i386up, он довольно-таки неэффективен в связи с высокими накладными расходами. Это объясняется тем, что вызов этих примитивов ядра подвешивается на какое-нибудь прерывание. Добавим сюда обычную смену привелегий ... И вот посылка сообщения уже тратит почти микросекунду только на int/iret (со сменой привелегий минимум 71 и 36 тактов соответственно, если Григорьев не врет). Далее требуется переключение задачи. Поскольку обычно используется tss - еще микросекунда. Далее требуется перепланировка - сложный планировшик с динамическим приоритетом - еще пара микросекунд. Ну и сам код со всякими прибамбасами типа записи статистики для последующего использования планировшиком - еще микросекунда (считая по 10ns на инструкцию). Да все это написано на Си. И так на каждом сообщении.
Совершенно очевидно (мне, по крайней мере), что для Элит такая стратегия не годится. Базовые системные примитивы должны выполняться _очень_ быстро. От их оптимизации будет зависеть производительность всей системы. Каждый лишний такт здесь обернется потерянными часами для всей системы (ну это я загнул конечно).
Основная идея состоит в следующем. Истинное микро-ядро (т.е. send, post, recv, reply и еще парочка примитивов, также отвечающих за IPC) находятся в начале адрессного пространства КАЖДОЙ задачи. Любая программа компилируется с базового адреса в 1k (ORG 400h). При загрузке в память, загрузчик копирует в этот промежуток первый килобайт из _своего_ собственного адресного пространства (а туда он в свою очередь заносится ядром - эдакий PSP). Вызов примитивов происходит косвенно через память - в самом начале ядреного килобайта лежит небольшая таблица переходов. Ближний вызов через память - это всего 5 тактов.
Таким образом, эти несколько функций не вшиты в пользовательские программы и могут быть изменены при изменении внутренних структур ядра. Кстати, а как эти функции могут получить доступ к тем самым структурам ? Просто:
mov ax,kern_data_selector
mov gs,ax
Переключение задачи также осуществляется очень быстро:
lss gs:[edi][task_stack]
...
iret
Фактически из всех задумчивых команд процессора используется одна единственная IRET (около 15 тактов). Адрес, по которому она переходит, сохраняется не при помощи еще более медлительных INT и CALL FAR, а через:
pushfd
cli ;далее пойдет обращение к структурам ядра
push cs
push ret_address
Заметим, что так реализованы лишь ключевые примитивы ядра. Более сложная и менее критичная по времени поддержка системных структур (например, создание селекторов) реализована как посылка сообщений отдельной задаче kern.
Эта оптимизация практически не уменьшает защищенности системы. Если программа затрет свой первый килобайт, - плохо будет ей одной (у всех остальных свои локальные копии этого кода). По ошибке обратиться к системным данным она тоже не может, т.к. регистр GS зарезервирован за системой и обычные программы никогда к нему не обращаются за пределами кода в ядреном килобайте. Можно, конечно, представить себе сценарий, при котором пошедшая вразнос программа передаст управление куда-нибудь в середину send, но если это место будет находится до загрузки регистра GS, то наихудший вариант будет заключаться в посылке неверного сообщения, которое должно быть отфильтровано получателем (sanity check). Если же управление попадет на команду после инструкции, загружающей GS, любая попытка его использования приведет к GP, т.к. за пределами микро-ядра GS всегда содержит 0. Вариантом, когда программа западет в аккурат на "mov gs,ax" можно пренебречь как крайне мало вероятным (ведь помимо этого в ax должен еще содержаться действительный селектор).
Имеющиеся недостатки
Этой операционной системы не существует.![]()
![]()
Есть несколько эскизов. Есть скелет компилятора с чего-то напоминающего Си с классами в стиле delphi pascal (а не С++). Все же скучно писать os на ассемблере. Вон некоторые уже в своем дебуге, говорят, ничего понять не могутЕсть много идей (Идея фикс: реакция системы должна быть как в транспьютере - 640ns). Есть этот проект ...
Comments are welcomed !
Code: Select all
XOR EAX,EAX
XOR EDX,EDX
MOV ECX,10h ;код обращения к TSC
WRMSR ;обнуление счетчика
... ;фрагмент программы
RDTSC ;считывание счетчика, старшие биты в EDX, младшие в EAX
Code: Select all
MOV ECX,10h ;код обращения к TSC
RDMSR ;чтение счетчика
Code: Select all
dw 310Fh ;код команды RDTSC
Code: Select all
RDTSC macro
dw 310Fh
endm
Code: Select all
MOV EAX,CR4
OR AL,08h ;3-й бит регистра CR4
MOV CR4,EAX
А НЕ НАПИСАТЬ ЛИ НАМ СОБСТВЕННЫЙ ДРАЙВЕР HARD ДИСКА...
Название, так же, подразумевает такие хорошие веши как PCI IDE Bus Master, всевозможные UDMA, и защищенный 32-битный режим.
Много горьких слов сказано про INT 13h появившийся на свет более 20 лет назад в 16-битную эпоху, и с тех пор натягиваемый на растущие как на дрожжах объемы дисковой памяти, как будто он сделан из сырой резины. Изначально заложеное в него ограничение на 1024 допустимых цилиндра породило первый барьер в 528М дисковой памяти. Выворачиваться пришлось разработчикам интерфейса, которые разродились LBA трансляцией адреса. Главное ее достижение, это линейная 28-битная адресация секторов, делающая ненужными такие понятия как Сектор, Головка, Цилиндр. Ими теперь занимается процессор Hard диска. Однако INT 13h прочно ставший стандартом BIOS требовал именно сектора, головки и цилиндры в качестве передаваемых параметров, посему далее прогресс в этой области пошел не только боком, но и раком, в каковом виде он до сих пор и находится.
LBA трансляция для преодоления ограничения INT 13h в 1024 цилиндра буквально пересчитывает цилиндры в головки с целью уменьшения количества первых за счет увеличения количества последних. Однако решение было не лучшим, так как было временным и на размере 8.4G пересчетные возможности LBA оказались исчерпанными, а диски тем временем перевалили за десятки гигабайт. Собственно ATA стандарт позволяет адресовать 136.9G на каждый диск. Более того два бита в адресе не задействованы, что позволит учетверить, а в случае передачи бита LBA/CHS на адресацию и увосьмерить эту цифру. Разработчики вероятно зарезервировали 2 бита из-за неопределенности, что потребует стандарт в будущем - увеличивать адреса или количество устройств (их сейчас два на канал). Собственно решение уже очевидно - адрес. Пока же в INT 13h добавили несколько новых функций, наконец, позволяющих работать с дисками более 8.4G. Правда вызов их, на взгляд автора, организован довольно дурацки, но главный недостаток не в этом.
Компьютеры давно стали 32-битными. Это значит, что возможна прямая работа с оперативной памятью до 4G. В 16-битном режиме допускалось только 64К - больше только с переключением сегментов, но не более 1М. INT 13h будучи, как и почти весь BIOS, 16-битным, может работать лишь в пределах первого мегабайта и его не колышет, что у вас этих мегабайт, к примеру, сотни. Это значит, что вам для передачи данных выше первого мегабайта потребуются повторные, причем совершенно неоправданные, передачи память - память. 32-битной альтернативы INT 13h в BIOS, на сегодняшний день нет. А между тем, в режимах PIO и PCI IDE Bus Master адрес можно, а иногда и нужно указывать 32-битным. Возможно и использование старого DMA контроллера, но он застыл в развитии на уровне PC AT и адресом более 16М разродиться тоже не способен и его присуствие в архитектуре, скорее всего лишь дань совместимости. Конечно, если вы работаете в любой современной 32-битной ОС, то вас этот момент не затрагивает. Все они снабжаются полноценными 32-битными драйверами. Однако наряду с достоинствами, эти ОС имеют и недостатки, в частности невозможность монополизации ресурсов процессора и памяти для решения только одной задачи. Подробнее этот момент расписан в статье "Отрицательные стороны многозадачности ОС" которую можно найти на страничке автора:
http://users.caucasus.net/dosextender
Если вы пришли к выводу, что для вашей задачи необходима однозадачная среда, то остается работа только из под Dos Extender-а, каковой из-за отсуствия дисковых драйверов общего назначения, вынужден использовать INT 13h со всеми его недостатками. За основу, в данном случае, взят авторский Dos Extender, который можно найти на вышеупомянутой страничке, благо он в исходных текстах и с отладчиком. К тому же, у него запрещено страничное преобразование, которое резко усложнит драйвер для DMA режимов (Не возмущайтесь, если заметите в основе его Dos Extender от 1993г. некоего Tran'a. Его авторство многократно упомянуо, более того, приведен и оригинальный его вариант). Впрочем, своя рука владыка, так что Dos Extender выбирайте какой вам больше нравится.
Сразу надо определить - нужен ли драйвер вообще. Во-первых, многим задачам скорость дисковых операций безразлична, они могут составлять лишь малую толику от общих затрат времени, так чего огород городить. Во-вторых, файловая система ДОС работает только через INT 13h и ваша задача, с собственным драйвером, должна работать минуя файловую систему - непосредственно с диском на уровне физического доступа к секторам, а это может потребовать создание собственного раздела с какой-либо разбивкой заменяющей файловую систему, пусть даже на примитивном уровне, но удовлетворяющем нужды задачи.
Предположим, вы пришли к выводу, что задача действительно нуждается в драйвере. Но в каком? Существующие протоколы обмена позволяют:
PIO - программировать попроще, но занят процессор, зато если ваш Dos Extender использует страничное преобразование, за это отдувается процессор. Однако, скорость обмена выше 16M/s вам не светит, хотя не теряется время на очистку кеш памяти при обмене - все данные и так достоверны.
PCI IDE Bus Master - программируется посложнее, но позволяет процессору во время дисковых операций, делать еще что-то хорошее. Если контроллер и сам Hard диск поддерживают режимы UDMA33, 66 или 100 то и скорость обмена вы получите соответствующую. Правда завершать операцию чтения (а операцию записи наоборот предварять) должна команда полной очистки кеша WBINVD, т.к. обмен данными происходит непосредственно с памятью, которая может не совпадать с содержимым кеша с Обратной записью (кеш первого уровня всегда такой, кроме 486 процессоров). Процедура это достаточно долгая и не портит общую картинку только в случае передачи достаточно больших объемов данных. Если же вас угораздило работать в режиме страничного преобразования - вот тут-то оно кровь вам и выпьет. Извольте пересчитать все адреса из линейных в физические, впрочем, этого нехорошего момента мы касаться не будем - у автора на страничное преобразование аллергия.
Предположим, что более привлекательным сочтен Bus Master протокол и ваш Hard диск поддерживает UDMA, тем более что 100, 66 или 33 явно больше чем 16. Остается решить, должен ли процессор во время дисковых операций еще что-то делать. Int 13h этого принципиально не позволяет. Он монопольно занимает процессорное время, часть которого составляет пустое ожидание. Драйвер с аналогичным поведением будет несомненно проще, поэтому на нем и остановимся. Сделать его посложнее, с параллельной работой, можно, но ой-ли нужно! Например для задачи поиска некоего контекста на диске, без использования параллельной работы результаты таковы: чтение с диска в память и последующий поиск в памяти, на 10 - 15% длятся дольше чем просто чтение с диска, безо всякого поиска, т.е. выигрыш за счет параллельной работы и составит эти несчастные проценты. Да не стоят они того!!! Абсолютные же показатели таковы: подобное сканирование диска 10G заняло около 15 минут в режиме UDMA33, дешевый процессор AMD K6-2/350, чипсет VIA MVP3, 128M памяти. Средняя скорость передачи 13M/s. Для сравнения, там же DiskEditor аналогичный контекст жевал примерно час, в пределах 1G !!! (на большее терпения не хватило). Вообще, премущество даже не в скорости, а в том, что имея исходный текст, контекст поиска можно сделать составным и вообще сколь угодно сложным.
Самый тяжелый момент параллельной работы - синхронизация процессов. Параллелить несколько дисковых операций, например при наличии двух Hard дисков, не рекомендуется, хотя наличие двух каналов у контроллера, это позволяет. Затраты на арбитраж PCI шины могут "съесть" скорость (исключение может составить довольно экзотичная задача переписи данных с диска подключенного к одному контроллеру на диск подключенный к другому. В этом случае выборка данных (не занимающая шину) диска с которого производиться чтение, может производиться одновременно с передачей данных (от предыдущего чтения) на записываемый диск. Вторая фаза - передача считанных данных будет производиться одновременно с выполнением вторым диском операции записи. Кстати, для подобной задачи не нужно объявлять недостоверным кеш. Данные не используются процессором, а контроллеры оперируют непосредственно с памятью).
Для осуществления параллельной работы скорее всего потребуется создание небольшой управляющей надстройки, своеобразного "супервизора" которая и будет распределять задания процессору и дисковому контроллеру, отслеживать их очередность и завершение, но это уже выходит за рамки драйвера. Дисковые операции придется ставить на контроль таймеру во избежание зависания при ошибочных или сбойных ситуациях (прерывания ведь можно и не дождаться).
Для тех кому параллельная работа не нужна, спецификации начиная с ATA-4 позволяют уменьшать затраты времени на ожидание готовности данных к передаче используя набор команд с поддержкой очередей (Queue). Последующую команду можно выставлять еще до того как отработает предыдущая. Однако программирование с их использованием чересчур усложнилось и некоторые производители предпочли путь повышениея эффективности обычных команд, т.е. при интенсивном обращении к длинным цепочкам секторов, последующие данные начинают подготавливатся заранее, еще до поступления на них запроса (вообще-то, документального подтверждения этому у автора нет, но временные замеры операций наводят на подобные мысли). В общем, диск которым располагает автор (WD 10G), эти команды не поддерживает.
Однако, немного коснемся пресловутого PCI IDE Bus Master. С чем его едят? Познакомиться с ним можно по первоисточнику - стандарту SFF-8038i. PCI шина кроме прочих своих обязанностей, стала 32-битной альтернативой контроллеру DMA, который создавался для 16-разрядной ISA шины и поэтому исходно мог передавать за один присест до 64К данных в пределах 1 мегабайта доступных ранним PC XT. С появлением 286 PC AT адресация расширилась до 16М за счет увеличения шины данных до 24 разрядов, но порции остались прежними. Режим Bus Master для устройств PCI как раз и выполняет задачу передачи данных в 32-разрядном адресном пространстве НЕ ЗАНИМАЯ ПРОЦЕССОР. Порции разовой передачи в принципе не ограничены, но могут иметь, определяемый контроллером устройства, предел. Для IDE контроллера, разовая передача осталась в принципе те же 64К байт, однако общая передача может составить 512M без дополнительного вмешательства, контроллер все сделает за вас. На архитектуру контроллера вероятно наложило свой дурной отпечаток, пресловутое страничное преобразование. В общем, он спроектирован под задачу - раскидать различные сектора с диска по РАЗНЫМ фрагментам памяти. Вроде бы, с точки зрения обычной задачи намерение довольно идиотское, но если исходить из структуры FAT (практически во всех ОС файлы хранятся не сплошным куском, а раскиданы по диску кластерами) или назначения SWAP-файла (см.Виртуальная память), то все становится на свои места. Работает это так. В памяти создается специальная таблица неких Дескрипторов (Physical Region Descriptor или PRD). Каждый дескриптор это 2 двухсловных (DWORD) элемента, где первый задает начальный адрес региона передачи, а второй счетчик байтов. Счетчик байтов не должен превышать 64К. Обший размер таблицы тоже не должен превышать 64К, т.е. дескрипторов должно быть не больше 8К, что и дает максимальный размер одной операции 512M. Признаком последнего Дескриптора таблицы является 1 в старшем (31-ом) бите счетчика байтов. Еще одно ограничение. Таблица, мало того должна быть выровнена в памяти на 4-х байтную границу, но и не должна пересекать 64К границу физической памяти. Практическое использование могут иметь таблицы из максимум двух Дескрипторов. Дело в том, что хоть контроллер и позволяет передавать 512M, но сами диски имеют ограничение одной операции - максимум 256 секторов, что дает 128K, а для их передачи достаточно двух Дескрипторов. Контроллер программируется через три регистра, а именно Команд, Состояния и Указателя на таблицу Дескрипторов. Регистры первого и второго контроллеров раздельные, что позволяет им работать независимо, а при желании и одновременно. Базовый адрес регистров получить довольно хлопотно. Необходимо вызывать функции конфигурации PCI устройств из BIOS32 Directory Service. Хоть и не предполагалось, но подпрограмма запроса этого адреса все же сделана и включена в драйвер. Поскольку для каждого компьютера это достаточно определить раз в жизни, впоследствии в программах для себя этот фрагмент можно опустить и задавать адрес как константу.
Предположим, базовый адрес у нас E000h.
Регистры распределятся так:
E000h - Регистр Команд. Размер BYTE.
- Бит 3 - Направление передачи. Для чтения диска - 1, для записи - 0.
- Бит 0 - Запись в него 1 вызывает старт операции передачи. После завершения операции его необходимо сбросить.
E002h - Регистр Состояния. Размер BYTE.
- Остальные биты не используются и в них должны быть 0.
- Бит 7 - Информационный. 0 в нем показывает, что оба контроллера способны работать одновременно.
- Бит 6 - Должен показывать, что диск Slave первого контроллера способен работать в DMA режимах. Взводиться должен, судя по всему, программой инициализации. Толку от него нет. Какой из ваших дисков поддерживает DMA вы и без него знаете. (надо заметить что поддерживаться должен, как минимум, режим Multiword DMA Mode 0 или более шустрые разновидности Mode 1, Mode 2, UDMA33, UDMA66). Можно и взвести для порядку.
- Бит 5 - То же, что Бит 6, но для диска Master первого контроллера.
- Бит 2 - Interrupt. Показывает, что диск выставил сигнал прерывания. Это сигнал КОНТРОЛЛЕРУ, что диск приготовился к обмену, а уж после обмена, контроллер выставит, уже свое прерывание процессору. Прерывание от диска попадает сразу на процессор только в PIO режимах. После выполнения операции его необходимо обнулить записав в него 1 (а не 0 )
- Бит 1 - Error. Ну, что это такое - ежу понятно. Для очистки этого бита необходимо записать в него 1, подобно биту 2.
- Бит 0 - Bus Master active. Смысл тоже ясен. Взводиться сам, как только взведен стартовый бит в командном регистре. Сбрасывается тоже сам, как только завершены все передачи заданые в таблице или сброшен стартовый бит. Если вы не хотите работать по прерываниям, можете анализировать этот бит, но параллельная работа в этом случае исключается. Замечена странная вешь. Прерывание означающее конец операции происходит несколько позже, чем сбрасывается этот бит и немалое время задержки пропорционально количеству передаваемых байт.
E004h - Регистр Указателя на таблицу Дескрипторов (PRDT). Размер DWORD. Младшие два бита должны содержать 0.
- Остальные биты не используются и в них должны быть 0.
Для Второго контроллера все аналогично:
- E008h - Регистр Команд.
- E00Ah - Регистр Состояния.
Нельзя не заметить, насколько сложной должна быть внутренняя структура такого контроллера. Это фактически должен быть специализированный процессор, хотя для выполнения этой же задачи (без бестолковых таблиц и Дескрипторов) вполне достаточно простого конечного автомата.
- E00Ch - Регистр Указателя на таблицу Дескрипторов (PRDT).
Раз уж взялись рассматривать программное назначение регистров, то неплохо бы за компанию, вспомнить и функции регистров самого IDE контроллера, которые управляют непосредственно диском, в то время как IDE Bus Master только передачей данных. Эти регистры, так же разбиты на две группы - каждая своему контроллеру. Адреса 1F0h - 1F7h для первого контроллера, 170h - 177h для второго. Надо сказать, что функции IDE контроллера расписаны в ATA стандартах, и для задействования UDMA режимов необходимо пользоваться хотя бы версией ATA-4, хотя есть и ATA-5. Обоими стандартами можно разжиться на страничке:
http://www.halyava.ru/document/ind_hard.htm
Расширения этих стандартов ATAPI в данном случае интереса не представляют и применяются в устройствах где стандартного набора регистров IDE контроллера не хватает и введено расширение ATA Packed Interface, например, для CD-ROM.
1F0h - R/W. Данные. Используется только в PIO режимах. Размер WORD.
1F1h - RO. Error. В случае ошибочных ситуаций, содержит тип ошибки. BYTE.
1F1h - WO. Features register. Передает дополнительные параметры некоторых комманд. Размер BYTE.
1F2h - RW. Счетчик секторов. При нулевом значении регистра, передается возможный максимум - 256 секторов. Размер BYTE.
1F3h - RW. Номер сектора. Нулевое значение регистра запрещено. Сектора считаются с 1. Допустимо 1 - 256. Размер BYTE.
1F4h - RW. Цилиндр. Младший байт.
1F5h - RW. Цилиндр. Старший байт. Всего цилиндров допустимо 0 - 65535.
1F6h - RW. Устройство/Головка. Размер BYTE.
- 0 - 3 биты задают номер головки 0 - 15.
- 4 бит - задает устройство Master/Slave - 0/1.
- 6 бит - LBA трансляция. Для CHS должен быть 0.
1F7h - RO. Status register. Размер BYTE.
- 7 и 5 биты - не используются и должны быть 1.
[/list]7 бит - Busy т.е. Занято. Очень важный бит (может также использоваться в общественных сортирах). Пока этот бит 1, устройство выполняет операцию и ни черта не видит и не слышит. Посему, остальные биты этого регистра в это время недостоверны.[/list][/list]5 бит - Device Fault. Прямая противоположность предыдущенго бита. Если этот бит взводиться, то дальнейшие действия могут быть следующими: Device отправляется либо в мусорный ящик, либо в мастерскую. Первое предпочтительнее (ближе, а результат тот же).[/list]
- 6 бит - Device Ready. Усегда Готов.
[/list]3 бит - Data Request. Фактически это сигнал запроса прерывания, т.е. устройство готово к передаче данных. В PIO режимах попадает сразу на процессор, а в IDE Bus Master на контроллер, а уж он после передачи выставляет прерывание процессору.[/list]
- 4 бит - Device Seek Complete. Используется только в тестах.
- 2 бит - Corrected Data. Сообщает, что считанные данные подверглись исправлению от ошибок, однако достоверны. Может быть задействован в задачах повышенной важности, т.к. возможности исправления не абсолютны, хотя и достаточно эффективны. Может также сигнализировать о приближении момента взведения 5 бита, со всеми вытекающими последствиями. Правда S.M.A.R.T это делает лучше.
- 1 бит - Index. Атавизм (например, хвост у человека или бесхвостая обезьяна).
ВНИМАНИЕ! Чтение этого регистра, является одновременно сигналом подтверждения прерывания, который необходимо подавать при завершении обработки этого прерывания. Для того, чтобы не нарушить ход операции, этот регистр дублируется по адресу 3F7h. Это т.н. Alternate Status register. Его чтение можно производить в произвольный момент времени, подтверждения прерывания это не вызывает.
- 0 бит - Error. Тип ошибки можно посмотреть в одноименном регистре.
1F7h - WO. Command register. Размер BYTE.
Команд в общем-то чортова пропасть, однако сейчас интерес представляют следующие:
- C8h - Read DMA with retries
Понятие with retries и without retries относятся к сбойным ситуациям, и подразумевают наличие или отсуствие повторных попыток успешно завершить операцию. Смысл примерно такой же, как и бита Corrected Data. Обычно используются команды with retries, тем более что команды
- CAh - Write DMA with retries
- C9h - Read DMA without retries
и им подобные, в стандарте ATA-4 уже исключены как анахронизм.
- CBh - Write DMA without retries
Итак, какова последовательность действий выполнения, например, операции чтения. Для режимов Bus Master, ее осуществляют два различных устройства, собственно IDE контроллер управляющий действиями Hard диска, и IDE Bus Master контроллер, занимающийся передачей данных.
1. Проверяются всевозможные регистры состояний на предмет того, что бы все было свободно для выполнения новой операции. У IDE контроллера проверять нужно ТОЛЬКО Альтернативный регистр состояния 3F7h/(377h), чтоб не выдать сигнал подтверждения прерывания до окончания предыдущей операции.
2. Инициализируются регистры IDE контроллера задающие цилиндры/головки/сектора и количество секторов предназначеных к передаче. Затем, можно сразу же выдать и код команды, что приведет к немедленному началу операции, и c задержкой 400ns взведет бит Busy.
3. Необходимо подготовить к работе IDE Bus Master. В ФИЗИЧЕСКОЙ памяти готовиться Таблица PRDT. На этот момент можно запретить кеш регистром CR0, это будет быстрее чем команда WBINVD. Адрес таблицы заносится в регистр Указателя на таблицу Дескрипторов E004h/(E00Ch), хотя он может заноситься туда однократно при инициализации драйвера.
4. Устанавливается бит направления в регистре команд E000h/(E008h) и одновременно взводится стартовый бит, что начинает операцию передачи. В этот момент взводится бит Active.
Происходит все следующим образом: Hard диск получив команду, позиционирует головки на нужный цилиндр, а затем списывает данные во внутренний буфер. Покончив с этим, он выставляет IDE Bus Master контроллеру, прерывание, мол, у меня все готово. Получив такой сигнал, IDE Bus Master контроллер начинает операцию передачи. В настоящий момент не выяснено, что происходит дальше, ибо бит Active через некоторое время сбрасывается, что должно свидетельствовать об окончании передачи, но прерывание процессору выставляется с задержкой, пропорциональной количеству передаваемых байт. Для работы драйвера, вообще-то, это безразлично, но непонятно, а следовательно неприятно. Обработчик прерывания должен выполнить несколько подтверждений прерываний и сбросить некоторые биты.
Собственно драйвер состоит из трех частей.
- Инициализация всего и вся.
- Подпрограмма работы с диском на физическом уровне.
Ну, по порядку.
- Подпрограмма работы на логическом уровне, в пределах выбраного раздела.
Простейшая инициализация предполагала, только перехват прерываний 14 и 15, и проверку готовности устройств. Базовый адрес Bus Master контроллера и количество секторов на диске предполагалось вносить как константы, как величины постоянные для конкретной машины. Впоследствии встала проблема мобильности программы, и в добавились подпрограммы автоматического определения этих значений (если работать только на конкретной машине, для простоты, можно вернуться к константам). Вот они:
- CommandIdeBusMast dw ? ;See PCI Configuration space
- StatusIdeBusMast dw ? ;CommandIdeBusMast + 2
- PRDTIdeBusMast dw ? ;CommandIdeBusMast + 4
- CommandIdeBusMast1 dw ? ;CommandIdeBusMast + 8
- StatusIdeBusMast1 dw ? ;CommandIdeBusMast + 10
- PRDTIdeBusMast1 dw ? ;CommandIdeBusMast + 12
- CapasityDrive0 dd ? ;Words 60-61 of IDENTIFY DEVICE
- CapasityDrive1 dd ? ;0 - if device not present
- CapasityDrive2 dd ? ;Set in InitDrvIDE
- CapasityDrive3 dd ?
Адрес Bus Master определяется через BIOS32 Directory Service, по методике описаной в SFF-8038i. Описание BIOS32 имеется только в PostScript и его можно только распечатать, и только на принтере с поддержкой этого формата. Возможно стандарты будут выложены автором на своей страничке т.к. собирать их по halyav'е хлопотно, а включать в комплект драйвера - лишние 160K архива.
Подробно вызова этих функций касаться не будем, единственно что желательно упомянуть, это особенности дальних вызовов этих функций. Они требуют загрузки в DS дескриптора адресующего ВСЮ память от 0 до 4G. В данном Dos Extender'е такой дескриптор 18h. Посему вызов (через память) приходится проводить по ES, равному текущему дескриптору 10h.
Возникает еще один вопрос инициализации. Кто определяет скорость? Например реальний средний трансфер 13M/s показываемый UDMA33 вызывает подозрение в некоем надувательстве или некорректной установке скорости. Конечно из среднего трансфера нужно выкинуть затраты времени на позиционирование головок, но замеры скорости от момента поступления прерывания контроллеру, с чего начинается собственно момент передачи данных, до прерывания процессору - на чем он собственно заканчивается, показывают пиковое значение всего лишь 17M/s (а может это и есть пресловутое 16.6), что все равно гораздо ниже 33. В чем там дело, объяснить затруднительно - нет литературы освещающей этот момент. Однако в журнальных обзорах производительности мат.плат, для UDMA33 цифра 13M/s подтверждается. Может арбитраж PCI шины "отъедает" себе кусок, может синхронизация памяти PC100 и некратная ей скорость PCI 33M/s. Это сейчас неважно. Чем же вообще задать эту скорость, например принудительно понизить.
Официально существует два пути.
Со стороны IDE контроллера, командой SET FEATURES. Со стороны чипсета, установкой соответствующих бит PCI IDE контроллера, но это требует описания установок чипсета. Что на это сказать - "Пробовали! Не получается!" Запрет UDMA в SETUP тоже игнорируется, правда он использует те же биты чипсета. Трансфер держится на 13, как будто других значений в природе не сущесвует. Может это связано с тем, что понижать скорость ниже UDMA33 нельзя, т.к. более медленный DMA Mode 2 с 16.6 M/s работает с другими сигналами на IDE шине (для режимов UDMA, часть управляющих сигналов в кабеле, меняет свое назначение). Может для ухода с UDMA66 на 33 и сработает? Кстати, если у вас нет UDMA, но есть PCI IDE Bus Master (инициализация это проверяет), то вы можете использовать данный драйвер, но в упомянутом DMA Mode 2.
Далее проверяется наличие дисков в системе и их емкость, точнее количество секторов. Это практически то, для чего раньше использовался CMOS. Хранимые в нем типы Hard дисков, а позднее их подробные параметры как то Цилиндры, Головки, Сектора, на размере более 8.4G потеряли смысл и ставятся на максимально рекомендованые значения (не превышать 1024 цилиндра). Единственно значащим параметром становится общее количество секторов. Все данные о диске прописаны в таблице параметров, которые можно получить по команде IDENTIFY DEVICE. Работает только в PIO режиме. В словах 60-61 (десятичное) и находится искомое количество секторов. Даже если в CMOS указать на диск NONE, то диск все равно будет корректно определятся. Ныне CMOS драйверу не указ. Windows'ы 9Х, кстати, так и поступают.
Для простоты драйвера, предполагается, что все исправно и корректно работает. Традиционные драйвера, при инициализации, не получая сигнала готовности от устройства, подают ему команду сброса и повторяют попытку. Данный драйвер, в этом случае, просто считает устройство несуществующим.
Из-за того, что стартовый адрес Dos Extender'а в памяти может менятся и определяется значением _code32a, требуется контроль за границей PRDT. В случае пересечения 64К, берется смещеное значение PRDT.
- PRDT dd 21 dup (0) ;4 PRDT + 4 Shift if cross 64K + 13 Push buff
[/list]PRDT1 dd 21 dup (0) ;4 PRDT + 4 Shift if cross 64K + 13 Push buff[/list]
- AddrPRDT dd ? ;LOGICAL Address PRDT
13 DWORD резервируется для "пропихивания" значений в физическую память сквозь буферы чипсета (кеш на этот момент запрешается через CR0). Возможно это излишняя перестраховка, но описания где регламентировалось бы поведение чипсета в подобных ситуациях найти не удалось.
- AddrPRDT1 dd ? ;LOGICAL Address PRDT1
При работе по прерываниям необходимо предусматривать случай когда по каким-то причинам прерывание не происходит (например в программе употребилась команда CLI, а STI забыли). Необходимо во избежание зависания, как-то контролировать подобную ситуацию. Обычно для этого используется системный таймер, но перехватывать еще одно прерывание автору было лень, да и сказалась привычка запрещать таймеры для счетных задач, чтоб ресурсы дуром не отъедали (для этого служит вызов подпрограммы _setmask8259, у которой 1 в бите AX запрещает соответствующее прерывание). Для этой цели был выбран несколько экзотичный для прикладного программирования Time Stamp Counter. Это 64-битный регистр считающий такты процессора, и используемый обычно для оценки эффективности фрагментов программы. Это создает зависимость таймаута от тактовой частоты процессора, но важно наличие самого таймаута, а будет он 1 минуту или 1 секунду, уже не суть важно, ведь он отрабатывает только в нештатных ситуациях. Выбирая контрольный бит TSC можно менять таймаут под конкретный процессор. Например для вышеупомянутой машины, бит 31 TSC дает таймаут в 7 секунд. Биты старше 31 в данном варианте драйвера использовать нельзя, они попадают в регистр EDX, а контролируется только EAX. Задействовать одновременно оба контроллера под TSC невозможно, ведь TSC один. Правда перейти на системный таймер не столь уж трудно, но необходимости в этом у автора не возникало (между прочим, упомянутую выше ситуацию с CLI системный таймер отработать не в состоянии, ведь запрещаются ВСЕ прерывания, а TSC пожалуйста. Кстати, поэтому во всех многозадачных ОС, пользователю запрещена команда CLI). В общем, даже название примера драйвера так и осталось TSC.
Казалось бы, вместо таймаута достаточно зациклить опрос бита Active регистра состояния Bus Master контроллера, и нечего морочить голову, да и прерывания становятся вообще не нужны. Однако, сие не так. Постоянный опрос (Polling) порта в/в настолько загружает шину, что скорость передачи падает в 3 - 4 раза.
Ну и наконец, как делать вызов. Какие параметры передавать и что получать. Предложеный вариант похож на Int 13h, правда с существенной особенностью. Счетчик секторов может быть любого, не вылезающего за пределы диска, размера т.е. за один вызов могут идти множественные обращения. Как упоминалось, у контроллера счетчик всего лишь 8-разрядный.
ВАЖНО!!! В этой подпрограмме НЕ используется команда WBINVD очистки кеша. Она должна следовать за операцией чтения с диска и предварять операции записи. Команда эта долгая и в некоторых случаях ненужная. Например перепись файла с одного места на другое, или копирование дисков. Данные и так в физической памяти, процессор их не трогает, так зачем дуром время терять.
Данная подпрограмма пользуется упомянутыми выше константами, устанавливаемыми процедурой инициализации.Code: Select all
;------------------------------------------------------------------------------ ; IDEBusMast - Physical Read and Write. ;------------------------------------------------------------------------------ ;Input: LBA ONLY !!! ;EAX = Bits 31, 29 - reserved for future use, must be 0, ; Bit 28, 30 - unused, must be 0, ; Bits 27 - 0 - LBA Address, ;BH = 0 Read, BH = 2 Write, (No change! Other value damaged driver), ;BL = Drive (80h - Master 0, 81h - Slave 0, 82h - Master 1, 83h - Slave 1.), ;ECX = Sector Count, may be more than 256, ;EDI = Physical Memory Address for transfer data. ; ;Output: CF=0 Successfull, in EDI input value, other input registers cleared, ; CF=1 Error, in AL errorcode, input registers undefined. ;Software detect errors: ;AL = 1 Timeout over (No Interrupt), ;AL = 2 Invalid Drive number in BL, ;AL = 3 LBA Address + Sector Count too big for this drive, ;AL = 4 No drive, ;AL = 5 Invalid Op. code in BH. ;AL = 6 EDI Odd. (must be aligned 2) ;Hardware errors: ;AL = 10h Device not Ready, ;AL = 11h Device Busy, ;AL = 12h Not cleared DRQ (previous operate no complete), ;AL = 13h Device Fault, ;AL = 14h Abort command, ;AL = 15h Uncorrectable Read Error, ;AL = 16h UDMA CRC Error, ;AL = 17h Sector Address out of range, ;AL = 18h No Media (for removable), ;AL = 19h Media Change (for removable), ;AL = 1Ah Media Change Request (for removable), ;AL = 1Bh Write Protect (for removable), ;Bus Master errors: ;AL = 20h IDE Bus Master Active (previous Bus Master operate no complete), ;AL = 21h Error IDE Bus Master, ; ;AL = 40h Unrecognized Error. ;------------------------------------------------------------------------------
Однако работать прикладной программой прямо на физическом доступе к диску где есть и другая информация, опасно. Ошибка шутя угробит всю систему. Безопаснее работать в выделенном для задачи разделе. Для чужих разделов можно например назначить атрибут "только чтение", и даже ошибочная запись обойдется без последствий. Адресация в разделе относительная, с 0 в его начале. В традициях Dos раздел объявляется "Логическим диском" и ему присваивается буква. Самому создать "не Dos" раздел можно вручную DiskEditor'ом. Задав в Partition Table начальные и конечные значения Цилиндров и т.п., затем отмечаете раздел и даете команду Calculate Partition для пересчета секторов (две последние графы Partition). Вообще, рабочими для вас являются только эти две последние графы. Именно в них и сидят стартовые LBA адреса разделов (предпоследняя) и число секторов в разделе (последняя). Для себя, Partition Table можно вообще не модифицировать, все параметры раздела вручную вносятся в программе в их описатели. Пример создания описателей для четырех разделов ниже.
Здесь первые два раздела DOS, в них запись запрещена. Два последних раздела рабочие. Можно создать фрагмент програмы инициализации, которая будет сама переносить данные из Partition Table в эти описатели, но на данный момент нужды в этом не возникало. Незадействованный параметр PartitionType соответствующий индикатору файловой системы в Partition Table зарезервирован на случай продолжения развития этой программы и создания под нее собственной файловой системы (список номеров и их сооветствия типам FS прилагается к стандартам). Создаваемая в Голландии подобная простая однозадачная V2_OS остановила эти планы, хотя возможно и не окончательно. Буквы присваиваемые разделам должны идти подряд начиная с 'C' и являются фактически порядковыми номерами, что исключает произвольное назначение их, разделам (это отражено в ошибке 83h). Логический вызов таков:Code: Select all
Partitions dd 4 ;!!! How many partitions on all drives. ;Partition descriptor for drive 'C' PartitionLetter db 'C' ;!!! Caps only. Compared with BL in ReadWrite. PartitionDrive db 80h ;!!! 80h-Master0, 81h-Slave0, 82h-Ma.1, 83h-Sl.1 PartitionAttrib db 02h ;!!! 0 - Read/Write, 2 - Read Only, PartitionType db 06h ;??? Type partition in Master Boot Record (MBR) PartitionStart dd 3Fh ;!!! Start LBA address of partition PartitionSize dd 0FB000h ;!!! PartitionEnd dd 0FB040h ;!!! PartitionStart + PartitionSize + 1 ;Partition descriptor for drive 'D' db 'D' ;!!! PartitionLetter db 80h ;!!! PartitionDrive db 02h ;!!! PartitionAttrib db 05h ;??? PartitionType dd 0FB040h ;!!! PartitionStart dd 01F9F40h ;!!! PartitionSize dd 02F4F41h ;!!! PartitionStart + PartitionSize + 1 ;Partition descriptor for drive 'E' db 'E' ;!!! PartitionLetter db 80h ;!!! PartitionDrive db 0 ;!!! PartitionAttrib db 47h ;??? PartitionType (type 47h possible free) dd 0308946h ;!!! PartitionStart dd 07E3E42h ;!!! PartitionSize dd 0AEC789h ;!!! PartitionStart + PartitionSize + 1 ;Partition descriptor for drive 'F' db 'F' ;!!! PartitionLetter db 80h ;!!! PartitionDrive db 0 ;!!! PartitionAttrib db 47h ;??? PartitionType dd 0AEC789h ;!!! PartitionStart dd 08311A6h ;!!! PartitionSize dd 0131D930h ;!!! PartitionStart + PartitionSize + 1
В эту подпрограмму WBINVD включена.Code: Select all
;------------------------------------------------------------------------------ ; ReadWrite - Logical Read and Write (in partitions). ;============================================================================== ;Input: LBA ONLY !!! ;EAX = Bits 31 - 28 - unused ; Bits 27 - 0 - Start Sector Address ; (address in EAX calculate relative selected partition ; EAX = 0 - begin of partition), ;BH = 0 Read, BH = 2 Write, ;BL = Letter of Partition ('C' - First, 'D' - Second, 'E' - Third, etc.), ;ECX = Sector Count, may be more than 256, ;EDI = Physical Memory Address for transfer data (if you like logical - change) ; ;Output: CF=0 Successfull, in EDI input value, other input registers cleared, ; CF=1 Error, in AL errorcode, input registers undefined. ;Errorcode 1 - 40h see IDEBusMast. ;ReadWrite errors: ;AL = 80h Address + Sector Count too big for this partition. ;AL = 81h Invalid Letter in BL. ;AL = 82h No Partition with this Letter. ;AL = 83h No equal letters in BL and Partition descriptor. ;AL = 84h Write to Write Protected Partition. ;------------------------------------------------------------------------------
Пример драйвера и вызывающей его головной программы которая заполняет всю Extended память тем, что идет с начала диска 'C' приведен в файле TSC.ASM. Dos Extender с которым она должна компоноваться START32.OBJ. Если нужны его исходники - адрес авторской странички указан в начале этого текста, там же ReadMe к Dos Extender'у и отладчику. Программа вызывает после инициализации отладчик, что позволяет пошагово проследить ее работу. Правда пошагово отслеживать собственно момент передачи данных нужно очень быстро, чтоб не вылететь по таймауту. Для отключения отладки выкиньте из ассемблерного текста INT 1. Собственно драйвер можно выделить в отдельный obj, или вообще пхнуть его в "тело" Dos Extender'а. Для компиляции и компоновки использовался TASM. К сожалению автору так и не удалось адаптировать Dos Extender к NASM, который не хочет корректно считать EXTERNAL адреса. Попытка обратиться за помощью к создателям закончилась вообще неожиданно. Автору заблокировали доступ к страничке NASM (впрочем по информации встреченой в конференциях, автор оказался не единственным, кого постигла чаша сия)
И последнее. Программа не "обкатана" на возможные ошибки и к операциям записи следует подходить с осторожностью (см. начало этого текста). Впрочем у автора все записи даже на физическом уровне пока попадали именно туда куда следует. Работа в случае когда в системе несколько Hard дисков ВООБЩЕ пока не проверялась.
Если интересуетесь файловыми системами, и вообще структурами всевозможных ОС, то рекомендую великолепный их обзор, причем начиная с доисторической эпохи в книге "Операционные Системы" Т.Б. Большакова и Д.В. Иртегова, доступной как в HTML, так и LaTex формате на станичке:
http://www.cnit.nsu.ru/~fat/osbook.html
(они даже написали статью типа "опыт автоматического конвертирования tex -> html").
Колесницкий В.М.
E-mail: kolesnitsky@caucasus.net