Под программной архитектурой (или просто — архитектурой) устройства мы будем понимать совокупность тех структурных особенностей, которые влияют на работу программ с устройством. Например, форма разъема для подключения устройства не входит в его архитектуру, но количество и назначение линий в этом разъеме может в нее входить (если эти линии могут программно управляться).
Как правило, вместе с устройством поставляется его контроллер (адаптер), содержащий электронные схемы управления устройством. Конструктивно контроллер может представлять собой плату, вставляемую в разъем шины компьютера, либо может быть расположен в корпусе устройства. В любом случае программы работают с устройством через посредство его контроллера, а поэтому с точки зрения архитектуры нет различия между понятиями «устройство» и «контроллер устройства».
Классификация периферийных устройств может быть выполнена по различным признакам.
· Устройства последовательного доступа (sequential access) и устройства произвольного доступа (random access). Для последовательных устройств характерно наличие определенного естественного порядка данных, при этом обработка данных в ином порядке либо невозможна, либо крайне затруднена. Классическим примером являются магнитные ленты, для которых чтение и запись данных ведутся от начала ленты к концу, а попытка доступа в ином порядке потребует постоянной перемотки ленты, резко снижающей скорость работы. К устройствам последовательного доступа можно отнести также клавиатуру, мышь, принтер, модем.
Для устройств произвольного доступа возможно обращение к различным порциям данных в любом порядке, причем эффективность работы не зависит (или слабо зависит) от порядка обращения. Для таких устройств характерно наличие адресации данных и операции поиска нужного адреса. Наиболее известный пример — магнитные диски и другие дисковые устройства. Кроме того, к устройствам произвольного доступа можно отнести монитор ПК (там есть адресация точек-пикселов, хотя операция поиска не нужна).
· Символьные (байтовые) и блочные устройства. Для символьных устройств наименьшей порцией вводимых и выводимых данных является один байт. Для некоторых символьных устройств можно за одну операцию выполнить ввод или вывод любого (в разумных пределах) требуемого количества байт.
Для блочных устройств наименьшей порцией ввода/вывода, выполняемого за одно обращение к устройству, является один блок, равный, как правило, 2k байт. Типичным размером блока может быть 512 байт, 1K байт, 4K байт и т.п., в зависимости от конкретного устройства. Наиболее известные примеры блочных устройств — магнитные диски и магнитные ленты. Для диска понятие блока обычно совпадает с понятием сектора. В частности, для IBM-совместимых ПК сектор (блок) диска равен 512 байт.
Блочная архитектура обусловлена особенностями используемой среды и, кроме того, блочный ввод/вывод более эффективен для высокоскоростных устройств, поскольку при этом уменьшается относительная доля времени, расходуемая на подготовительные и заключительные операции при каждом обращении к устройству.
· Физические, логические и виртуальные устройства. Под физическим устройством обычно понимается некоторый реально существующий прибор, «железка». На самом деле, с точки зрения программной архитектуры для наличия физического устройства достаточно знать набор адресов, команд, прерываний и других сигналов, позволяющих выполнять операции с данными. Куда идут или откуда приходят эти сигналы — это вопрос, не касающийся программиста.
Логическое устройство — это понятие, характеризующее специальное назначение устройства в данной ОС. Например, «загрузочный диск» (т.е. тот, с которого была выполнена загрузка ОС). Наиболее важными логическими устройствами во многих ОС являются устройство стандартного ввода и устройство стандартного вывода. Их можно упрощенно определить как устройства, используемые для ввода и, соответственно, вывода «по умолчанию», т.е. когда в программе явно не указано другое устройство или файл для ввода/вывода. Как правило, для современных компьютеров устройству стандартного ввода соответствует физическое устройство — клавиатура, а устройству стандартного вывода — монитор. Важно, однако, понимать, что это соответствие может быть изменено: стандартный вывод может быть переназначен, например, на принтер или в файл, стандартный ввод — на удаленный терминал, на файл и т.п.
Понятие «виртуальный» в программировании, вообще говоря, означает примерно следующее: «нечто, на самом деле не существующее, но ведущее себя так, как если бы оно существовало». С этой точки зрения, виртуальное устройство — это программно реализованный объект, который ведет себя подобно некоторому физическому устройству, хотя на самом деле использует ресурсы совсем других устройств (или даже никаких устройств). Примеры виртуальных устройств весьма разнообразны:
- виртуальные диски, расположенные на самом деле в оперативной памяти (такие устройства были популярны в конце 80-х годов);
- виртуальная память, расположенная на самом деле на диске;
- виртуальные CD и DVD — программы, имитирующие поведение соответствующих устройств;
- виртуальный экран, предоставляемый DOS-программе, работающей в режиме окна Windows (программа работает так, как если бы ей был предоставлен весь экран, но на самом деле система направляет вывод программы в отведенное ей окно);
- самый забавный (но очень полезный) пример — пустое устройство, которому не соответствует никакая аппаратура. Почему его вообще можно назвать устройством? Потому что соответствующая системная программа (драйвер пустого устройства) корректно выполняет все действия, которые обязан выполнять драйвер устройства. Такое устройство безотказно принимает выходной поток символов (и тут же выбрасывает принятые данные), а также может использоваться для ввода, но при этом тут же сообщает — дескать, достигнут конец файла. Пустое устройство полезно в тех случаях, когда некоторая программа требует непременно указать файл или устройство для вывода объемных и не очень нужных данных. Кроме того, копирование файла на пустое устройство — это простой способ убедиться, что файл читается без ошибок.
3. Способы организации ввода/вывода. Синхронный и асинхронный ввод/вывод.
Архитектура подсистемы ввода/вывода
С программной точки зрения, устройство (или его контроллер) обычно представлено одним или несколькими регистрами. Регистр устройства — это адресуемое машинное слово, используемое для обмена данными или сигналами между устройством и процессором. Можно выделить два основных типа регистров.
· Регистр данных служит для обмена данными. Запись данных в такой регистр (если она возможна) означает вывод данных на устройство, чтение данных из регистра — ввод с устройства.
· Регистр управления и состояния содержит два типа двоичных разрядов (битов). Биты состояния служат для передачи процессору информации о текущем состоянии устройства (например, флагов готовности и ошибки, сигналов прерывания). Биты управления служат для передачи на устройство команд, позволяющих задать выполняемую операцию, запустить выполнение операции, установить режимы работы устройства и т.п.
В различных компьютерах используется один из двух способов адресации регистров устройств.
· Отображение регистров устройств на память. При этом способе для устройств отводится определенная часть адресного пространства памяти, а для работы с устройствами можно использовать те же команды, что и для работы с основной памятью (например, команду MOV).
· Адресация регистров через порты ввода/вывода. Для портов отводится отдельное адресное пространство, и для работы с ними имеются специальные команды (например, IN и OUT).
Первый способ удобнее для программирования, поскольку позволяет использовать более широкий набор команд. Однако этот способ труднее реализовать на аппаратном уровне, поскольку аппаратура должна определять, относится ли конкретный адрес к памяти или к устройству, и по-разному обрабатывать эти два случая.
Среди различных возможных конфигураций однопроцессорной вычислительной системы принято выделять два основных типа: системы с магистральной и с радиальной архитектурой.
· Магистральная архитектура основана на подключении всех имеющихся устройств, включая процессор и память, к единой системной магистрали (шине), которая объединяет в себе линии передачи данных, адресов и управляющих сигналов. Совместное использование магистрали различными устройствами подчиняется специальным правилам (протоколу), обеспечивающему корректность работы магистрали.
· Радиальная архитектура предполагает, что каждое из устройств, включая память, подключается к процессору отдельно, независимо от других устройств, и взаимодействует с процессором по собственным правилам.
Для программиста понятия магистральной и радиальной архитектуры имеют несколько иное содержание, чем для инженера-системотехника. С точки зрения программной архитектуры, неважно, подсоединено ли устройство к процессору напрямую или через посредство системной магистрали. Важно то, какие сигналы должна посылать и принимать программа, работающая с устройством, и какие команды могут для этого использоваться.
Основная особенность магистральной архитектуры — единообразный способ подключения всех устройств. Структура регистров устройства стандартизуется, при этом определяется, какими сигналами любое устройство может обмениваться с процессором и каким разрядам регистра должны соответствовать эти сигналы. Конечно, не всякое устройство нуждается в использовании всего набора стандартных сигналов. Некоторые типы устройств могут, например, не генерировать прерываний, не сообщать об ошибках. Но те сигналы, которые устройство использует, должны соответствовать стандарту данной магистрали.
Преимуществом магистральной архитектуры является простота подключения новых типов устройств, поэтому такая архитектура особенно удобна для открытых вычислительных систем, т.е. таких, которые рассчитаны на расширяемый набор периферийных устройств.
Напротив, для радиальной архитектуры характерен индивидуальный выбор способа подключения, наиболее удобного для каждого типа устройств. При этом в принципе можно достичь экономии аппаратных ресурсов и более высокой эффективности. Случается даже, что в одном порту объединяются управляющие сигналы от нескольких разных устройств. Очевидно, подобная архитектура удобна только в том случае, когда она рассчитана на постоянный набор устройств. Расширение радиальной системы всегда вызывает затруднения.
Исходя их этих определений, не так уж легко точно охарактеризовать современные IBM-совместимые ПК. Исходная модель IBM PC имела довольно четко выраженную радиальную архитектуру и небольшой набор стандартных устройств. В последующих моделях были сделаны значительные шаги по стандартизации подключения новых устройств. Однако и сегодня эти компьютеры не тянут на магистральную архитектуру в полном смысле слова: у них для этого слишком много разных шин.
Важной деталью архитектуры современных компьютеров является такое устройство, как контроллер прямого доступа к памяти (ПДП, англ. DMA — Direct Memory Access). Если обычно весь обмен данными идет через регистры процессора, то ПДП подразумевает прямой перенос данных с устройства в память или обратно. Роль процессора в данном случае только в том, чтобы инициировать операцию ввода/вывода блока данных, послав соответствующие команды контроллеру ПДП. Далее процессор не участвует в выполнении обмена данными. Завершив операцию, контроллер ПДП посылает сигнал прерывания, извещая об этом процессор. Это позволяет повысить производительность системы за счет частичной разгрузки процессора и магистрали.
Способы организации ввода/вывода
Ввод/вывод по опросу и по прерываниям
Рассмотрим более подробно работу программы, непосредственно выполняющей ввод или вывод данных на конкретное устройство. (На самом деле, этой работой обычно занимается драйвер устройства, так что мы фактически рассматриваем логику работы драйвера.)
Для определенности положим, что программа должна выдать N байт данных из массива A на символьное устройство X. Для операции ввода могут использоваться те же подходы, которые будут рассмотрены здесь для операции вывода.
Пусть архитектура устройства представлена регистром данных X.DATA и флагом готовности X.READY. Когда X.READY = TRUE, в регистр X.DATA можно выдавать очередной байт данных. Запишем на псевдокоде, близком к языку Паскаль, варианты организации соответствующей программы.
а) Ввод/вывод без проверки готовности
i:= 1;
while i <= N do begin
X.DATA:= A[i];
i:= i + 1;
End;
Этот «наглый» способ вывода вполне работоспособен, если используется «всегда готовое» устройство (например, монитор), т.е. флаг X.READY всегда истинен и потому вообще не нужен. При попытке использовать тот же подход для вывода на принтер мы убедились бы, что напечатаны будут лишь некоторые символы, которым посчастливилось быть выданными в редкие моменты готовности принтера.
б) Ввод/вывод по опросу готовности
i:= 1;
while i <= N do begin
While not X.READY do
;
X.DATA:= A[i];
i:= i + 1;
End;
Здесь добавлен цикл ожидания, в котором не делается ничего, кроме постоянной циклической проверки готовности устройства. Передача данных происходит только тогда, когда устройство готово. Поскольку после выдачи одного байта устройство вполне может опять перейти в состояние неготовности, следует опять выполнять цикл ожидания, пока выданный символ не будет обработан устройством.
Такая организация ввода/вывода позволяет корректно работать с любыми устройствами. Этот способ действительно применяется в некоторых однозадачных системах. Недостатком данного способа является непроизводительная трата времени на постоянное «долбление» флага готовности. При современном соотношении скоростей работы процессора и периферии, цикл ожидания может повторяться миллионы раз перед выдачей каждого байта. Более того, если по каким-то причинам устройство вообще не перейдет в состояние готовности, то работа всей системы может быть парализована бесконечным циклом ожидания.
в) Ввод/вывод по прерываниям
i:= 1;
while i <= N do begin
X_INT: if not X.READY
Return;
X.DATA:= A[i];
i:= i + 1;
End;
Здесь исчез цикл ожидания, вместо него — однократная проверка готовности и оператор возврата, если не готово.
Куда, собственно, происходит возврат? Чтобы это понять, надо вспомнить, что данный фрагмент — явно не единственная программа, работающая в данный момент на ЭВМ. Очевидно, операция вывода была начата операционной системой по запросу какой-то программы. Данный фрагмент был вызван как подпрограмма ОС, и возврат означает передачу управления ОС. Как система распорядится полученным временем? Это уже совсем другой вопрос, не связанный с вводом/выводом. Например, ОС может переключиться на другой процесс. Или, от нечего делать, запустить экранную заставку либо программу самотестирования.
Но как же быть с брошенной на полпути операцией вывода? Для ее возобновления будет использовано аппаратное прерывание, которое должно выдать устройство X при переходе в состояние готовности. Системный обработчик прерывания должен будет передать управление по адресу, обозначенному меткой X_INT. После нелишней дополнительной проверки готовности программа вывода передаст очередной байт на устройство, затем снова проверит готовность и, возможно, вновь вернет управление системе. Таким образом, выполнение ввода/вывода разбивается на отдельные интервалы работы при готовности устройства, перемежающиеся работой системы, пока устройство не готово.
Для устройств, использующих контроллер ПДП, возможные варианты организации работы остаются, по сути, теми же, но только используются гораздо более крупные операции: вместо ввода или вывода одного элемента данных выполняется ввод/вывод целого блока данных, и только после этого контроллер переходит в состояние готовности и генерирует прерывание.