Устройства графического вывода
Если устройства графического ввода (сканеры, видеограбберы, цифровые фотоаппараты) пока еще относительно редки (хотя, по мере развития цифровой фотографии, положение быстро меняется), то устройствами графического вывода снабжается каждый современный настольный и переносной компьютер. Многие встраиваемые приложения также имеют хотя бы небольшие, но дисплеи, чаще всего жидкокристаллические. Два основных практически применяемых типа дисплейных устройств – это электронно-лучевые трубки, используемые в кинескопах телевизоров и мониторах настольных компьютеров, и жидкокристаллические дисплеи. Устройства других типов – матрицы светодиодов и газоразрядные (“плазменные”) панели пока что дороги в производстве, либо не всегда приемлемы по качеству, поэтому в настоящее время еще не находят массового применения. Принцип действия ЭЛТ широко известен. Изображение в этих устройствах формируется катодным лучом – пучком электронов, испускаемых отрицательно заряженным электродом. Вспомогательные электроды и электромагниты фокусируют луч, а два набора управляемых электромагнитов – катушки горизонтальной и вертикальной развертки – отклоняют этот луч по вертикали и горизонтали. Попадая на лицевую поверхность трубки – экран – электроны заставляют светиться нанесенный на нее краситель-люминофор, формируя, таким образом, изображение. Изменяя напряжение на катоде, можно управлять яркостью луча и, соответственно, яркостью участка изображения. Люминофор светится еще какое-то время после ухода луча с него. Видеоконтроллеры, применяемые в современных персональных компьютерах и рабочих станциях, содержат более или менее сложные видеопроцессоры, способные без участия центрального процессора рисовать в видеобуфере различные графические примитивы, начиная от прямых линий и окружностей, и заканчивая проекциями и/или фотореалистичными изображениями трехмерных объектов, описываемых языком OpenGL.
Запоминающие устройства прямого доступа
Основную массу устройств этого типа составляют диски – жесткие и гибкие, магнитные, магнитооптические и оптические. Ассоциация между понятиями “постоянное запоминающее устройство прямого доступа” и “диск” столь плотно укоренилась в общественном сознании, что запоминающие устройства, основанные на иных принципах, например, устройства на флэш-памяти, иногда называют “твердотельными дисками”. Для хранения данных служит диск, покрытый ферромагнитным слоем. В современных накопителях запись осуществляется на обе стороны диска. Многие приводы с неудаляемыми дисками имеют несколько дисков. Диск может быть как гибким, так и жестким. В зависимости от способа крепления, диски делятся на съемные (удаляемые) и несъемные (фиксированные). В действительности, съемность накладывает серьезные ограничения не только на крепление диска к оси, но и на конструкцию блока головок и привода в целом, а также предъявляет определенные требования к контроллеру – тот должен отслеживать наличие носителя и, возможно, его параметры.
Драйверы внешних устройств
Драйвер (driver) представляет собой специализированный программный модуль, управляющий внешним устройством. Слово driver происходит от глагола to drive (вести) и переводится с английского языка как извозчик или шофер: тот, кто ведет транспортное средство. Драйверы обеспечивают единый интерфейс для доступа к различным устройствам, тем самым устраняя зависимость пользовательских программ и ядра ОС от особенностей аппаратуры. Драйвер не обязательно должен управлять каким-либо физическим устройством. Многие ОС предоставляют также драйверы виртуальных устройств или псевдоустройств – объектов, которые ведут себя аналогично устройству ввода-вывода, но не соответствуют никакому физическому устройству. В виде псевдоустройств реализуются трубы в системах семейства Unix и почтовые ящики в VMS. Еще одним примером полезного псевдоустройства являются устройства /dev/null в Unix и аналогичное ему \DEV\NUL в MS DOS\Windows\OS/2.
Прикладные программы, использующие собственные драйверы, не так уж редки – примерами таких программ могут быть GhostScript (свободно распространяемый интерпретатор языка PostScript, способный выводить программы на этом языке на различные устройства.
Большинство ОС общего назначения запрещают пользовательским программам непосредственный доступ к аппаратуре. Это делается для повышения надежности и обеспечения безопасности в многопользовательских системах. В таких системах драйверы являются для прикладных программ единственным способом доступа к внешнему миру. Еще одна важная функция драйвера – это взаимоисключение доступа к устройству в средах с вытесняющей многозадачностью. Допускать одновременный неконтролируемый доступ к устройству нескольких параллельно исполняющихся процессов просто нельзя, потому что для большинства внешних устройств даже простейшие операции ввода-вывода не являются атомарными. Например, в большинстве аппаратных реализации последовательного порта RS232 передача байта состоит из четырех шагов: записи значения в регистр данных, записи команды “передавать” в регистр команды, ожидания прерывания по концу передачи и проверки успешности передачи путем считывания статусного регистра устройства. Нарушение последовательности шагов может приводить к неприятным последствиям – например, перезапись регистра данных после подачи команды, но до завершения передачи, может привести к остановке передачи или, что еще хуже, передаче искаженных данных и т. д. Нельзя также забывать о неприятностях более высокого уровня – например, смешивании вывода разных процессов на печати или данных – на устройстве внешней памяти. Поэтому оказывается необходимо связать с каждым внешним устройством какой-то разграничитель доступа во времени. В современных ОС эта функция возлагается именно на драйвер. Обычно одна из нитей драйвера представляет собой процесс-монитор, выполняющий асинхронно поступающие запросы на доступ к устройству. В Unix, OS/2 и Windows NT/2000/XP этот процесс называется стратегической функцией. При определении интерфейса драйвера разработчики ОС должны найти правильный баланс между противоречивыми требованиями:
1. стремлением как можно сильнее упростить драйвер, чтобы облегчить его разработку и (косвенно) уменьшить вероятность опасных ошибок;
2. желанием предоставить гибкий и интеллектуальный интерфейс к разнообразным устройствам.
Драйверы обычно разрабатываются не поставщиками операционной системы, а сторонними фирмами – разработчиками и изготовителями периферийного оборудования. Поэтому интерфейс драйвера является ничуть не менее внешним, чем то, что обычно считается внешним интерфейсом ОС – интерфейс системных вызовов. Соответственно, к нему предъявляются те же требования, что и к любому другому внешнему интерфейсу: он должен быть достаточно простым, исчерпывающе документированным и стабильным – не меняться непредсказуемо от одной версии ОС к другой. Идеальным вариантом была бы полная совместимость драйверов хотя бы снизу вверх, чтобы драйвер предыдущей версии ОС мог использоваться со всеми последующими версиями. Потеря совместимости в данном случае означает, что все независимые изготовители оборудования должны будут обновить свои драйверы. Организация такого обновления оказывается сложной, неблагодарной и часто попросту невыполнимой задачей – например, потому, что изготовитель оборудования уже не существует как организация или отказался от поддержки данного устройства. Таким образом, интерфейс драйвера часто оказывается наиболее консервативной частью ОС. К сожалению – несмотря даже на то, что в общих чертах архитектура драйвера в большинстве современных ОС удивительно похожа – идея эта, по-видимому, нереализуема. Даже для близкородственных ОС – например, систем семейства Unix – драйверы одного и того же устройства не всегда могут быть легко перенесены из одной ОС в другую, не говоря уж о возможности использования без модификаций. Еще более удивительным является тот факт, что две линии ОС – Windows 95/98/МЕ и Windows NT/2000/XP – поставляемых одной и той же компанией Microsoft и реализующих почти один и тот же интерфейс системных вызовов, – Win32 – имеют совсем разный интерфейс драйвера. Проблема здесь в том, что интерфейс между драйвером и ядром ОС всегда двусторонний: не только прикладные программы и ядро вызывают функции драйвера, но и, наоборот, драйвер должен вызывать функции ядра. Таким образом, до тех пор, пока используются ОС различной архитектуры, разработка универсального интерфейса драйвера, если теоретически и возможна, то практически вряд ли осуществима.
Функции драйверов
Прежде всего, драйвер должен иметь функции, вызываемые ядром при загрузке и выгрузке модуля и при подключении модуля к конкретным устройствам. Например, в Sun Solaris это перечисленные функции:
1. – инициализация драйвера. Эта функция вызывается при загрузке модуля. Драйвер должен зарезервировать все необходимые ему системные ресурсы и проинициализировать собственные глобальные переменные. Инициализация устройства на этом этапе не происходит.
2. – проверить наличие устройства в системе. Во многих системах эта функция реализуется не самим драйвером, а специальным модулем-“сниффером” (sniffer – дословно, “нюхач”), используемым программой автоконфигурации.
3. – инициализация копии драйвера, управляющей конкретным устройством. Эту функцию можно рассматривать как аналог конструктора объекта в объектноориентированном программировании. Если в системе присутствует несколько устройств, управляемых одним драйвером, некоторые ОС загружают несколько копий кода драйвера, но в системах семейства Unix эта функция просто вызывается многократно.Каждая из инициализированных копий драйвера имеет собственный блок локальных переменных, в которых хранятся переменные состояния устройства. При вызове инициализации драйвер должен прочитать конфигурационный файл, где записаны параметры устройства (номенклатура этих параметров зависит от устройства и от драйвера), разместить и проинициализировать блок переменных состояния, зарегистрировать обработчики прерываний, проинициализировать само устройство и, наконец, зарегистрировать устройство как доступное для пользовательских программ, создав для него минорную запись (minor node). В ряде случаев драйвер создает для одного устройства несколько таких записей. Пример: в большинстве систем семейства Unix лентопротяжные устройства имеют две минорные записи. Одно из этих устройств при открытии перематывает ленту к началу, другое не перематывает. В действительности оба устройства управляются одним и тем же драйвером, который определяет текущий режим работы в зависимости от указанной минорной записи.
4. – деинициализация драйвера (аналог деструктора объекта в ООП). Впрочем, в отличие от деструктора, эта операция не безусловна – если не удается нормально завершить обрабатываемые в данный момент операции над устройством, драйвер может и даже обязан отказаться деинициализироваться. При деинициализации драйвер должен освободить все системные ресурсы, которые он занял при инициализации и в процессе работы (в том числе и уничтожить минорную запись) и может, если это необходимо, произвести какие-то операции над устройством, например, выключить приемопередатчик, запарковать головки чтения-записи и т. д. После того, как все устройства, управляемые драйвером, успешно деинициализированы, система может его выгрузить.
5. – выгрузка драйвера. Драйвер обязан освободить все ресурсы, которые он занял на этапе инициализации модуля, а также все ресурсы, занятые им во время работы на уровне модуля (не привязанные к конкретному управляемому устройству).
После того, как драйвер проинициализировался и зарегистрировал минорную запись, пользовательские программы могут начинать обращаться к нему и к управляемым им устройствам. Понятно, что обеспечить единый интерфейс к разнообразным категориям устройств сложно. Наиболее радикально подошли к этой проблеме разработчики системы UNIX, разделившие все устройства на два класса (блочные и последовательные) и определившие операции над устройствами каждого из них.
Многоуровневые драйверы
Нередка ситуация, когда драйвер не может осуществлять управление устройством полностью самостоятельно. Как пример можно рассмотреть драйвер лентопротяжного устройства конструктива SCSI. Для выполнения любой операции над устройством такой драйвер должен сформировать команду или последовательность команд SCSI, передать ее устройству, дождаться ответа и проанализировать его. Аналогичные ситуации возникают и с другими устройствами. Например, с IBM PC-совместимыми компьютерами могут работать три основных типа устройств позиционного ввода: мыши, использующие протокол обмена, совместимый с Microsoft Mouse, мыши с протоколом Logitech, планшеты и дигитайзеры с протоколом Summagraphics. Устройства всех этих, а также нескольких менее распространенных типов могут подсоединяться как минимум к четырем различным периферийным портам: специальному “мышиному” разъему PS/2, к последовательной шине USB и к последовательному порту RS232, причем в качестве порта RS232, может использоваться как один из четырех стандартных портов IBM PC, так и, например, один из выходов мультипортовой платы. В этом случае также целесообразно реализовать четыре самостоятельных драйвера транспортных портов (тем более что к этим портам могут подключаться и другие устройства) и три драйвера протоколов обмена, способных работать с любым транспортным портом. Объединение этих драйверов в одном модуле может создать некоторые – довольно, впрочем, сомнительные – удобства администратору системы, в которой используется стандартная комбинация порта и мыши, но поставит в безвыходное положение администратора системы, в которой комбинация нестандартная, например, не позволяя использовать мышь, подключенную к мультипортовой плате.
Загрузка драйверов
Чаще всего драйверы размещаются в адресном пространстве ядра системы, исполняются в высшем кольце защиты и имеют доступ для записи к сегментам данных пользовательских программ, и, как правило, к данным самого ядра. Это означает, что ошибка в драйвере может привести к разрушению пользовательских программ и самой ОС. Чтобы избежать этого, пришлось бы выделять каждому драйверу свое адресное пространство и обеспечивать обмен данными между драйвером, ядром и пользовательской программой посредством статических разделяемых буферов или динамического отображения блоков данных между разными адресными пространствами. Оба решения приводят к значительным накладным расходам, а второе еще и предъявляет своеобразные требования к архитектуре диспетчера памяти.
На практике иногда – особенно при использовании многоуровневых драйверов – оказывается возможным перенести отдельные функции работы с устройствами в контекст пользовательского процесса. Одна из относительно удачных попыток такого переноса была осуществлена в 70-е годы при разработке экранного редактора vi для ОС Unix. По замыслу разработчиков этот редактор должен был работать с большим количеством разных видеотерминалов, использовавших различные несовместимые системы команд для перемещения курсора и редактирования текста в буфере терминала и столь же несовместимые схемы кодирования специальных клавиш (стрелочек, функциональных клавиш и пр.). Как и в примере с мышами, разные терминалы могли подключаться к компьютеру через различные типы последовательных портов: “токовую петлю”, позднее – RS232 и др. В этом случае тоже было естественно разделить драйвер терминала на драйвер порта и модуль, занимающийся генерацией команд и анализом приходящих от терминала кодов расширенных клавиш. Вместо разработки отдельного модуля, решающего вторую часть задачи, была создана системная база данных для систем команд разных терминалов. Для работы с терминальной базой данных позднее было создано несколько библиотек подпрограмм. Для терминалов описанный подход оказался если и не идеальным, то, во всяком случае, приемлемым. Но, например, для графических устройств он не подошел – системы команд различных устройств оказались слишком непохожими и не сводимыми к единой системе “свойств”. В обоих случаях “драйвер” оказывается разбит на две части: собственно драйвер, исполняющийся в режиме ядра, который занимается только обменом данными с устройством, и программу, интерпретирующую полученные данные и/или формирующую команды для устройства. Эта программа может быть довольно сложной, но ошибка в ней не будет фатальной для системы, так как она исполняется в пользовательском кольце доступа. Проще всего происходит загрузка драйверов в системах, в которых ядро собирается в статический загрузочный модуль. В них драйвер просто присоединяется редактором связей к образу ядра и без каких-либо дополнительных усилий оказывается в памяти в процессе загрузки системы. В системах с динамической подгрузкой модулей ядра, драйвер представляет собой перемещаемый загрузочный или объектный модуль, иногда того же формата, что и стандартные объектные или загрузочные модули в системе, а иногда и специализированного. В этом случае ядро должно содержать редактор связей, возможно являющийся функциональным подмножеством полноценного системного линкера.
Оглавление
История появления. 2
Основные функции операционных систем. 2
Классификация операционных систем. 2
Семейства операционных систем. 4
Выбор операционной системы.. 4
Открытые системы.. 4
Представление данных в вычислительных системах. 4
Представление чисел. 4
Представление текстовых данных. 5
Представление изображений. 5
Прорисовка букв и цифр. Шрифты.. 6
Представление звуков. 7
Упаковка данных. 8
Контроль целостности информации. 10
Введение в криптографию.. 11
Загрузка программ. 15
Абсолютная загрузка. 16
Разделы памяти. 16
Относительная загрузка. 17
Базовая адресация. 18
Позиционно-независимый код. 18
Оверлеи (перекрытия) 19
Сборка программ. 19
Сборка в момент загрузки. 21
Динамические библиотеки. 21
Загрузка самой ОС.. 22
Управление оперативной памятью.. 23
Открытая память. 23
Алгоритмы динамического управления памятью.. 24
Сборка мусора. 25
Системы с базовой виртуальной адресацией. 26
Параллельное выполнение процессов. 28
Системы, управляемые событиями. 31
Windows 9X как пример системы, управляемой событиями. 35
Реализация многозадачности на однопроцессорных компьютерах. 37
Внешние устройства. 37
Доступ к внешним устройствам. 38
Простые внешние устройства. 40
Порты передачи данных. 41
Шины передачи данных. 43
Устройства графического вывода. 45
Запоминающие устройства прямого доступа. 45
Драйверы внешних устройств. 46
Функции драйверов. 48
Многоуровневые драйверы.. 49
Загрузка драйверов. 50