Индекс
НазадОглавлениеВперед


Глава 6
Управление вводом-выводом

6.1. Виртуализация устройств и структура драйвера

Управление вводом-выводом в полной мере воплощает в себе определение "ОС снаружи": ОС конструирует ресурсы высокого уровня - виртуальные устройства - и предоставляет пользователю интерфейс для работы с ними. Программисты, начинавшие работу в среде MS DOS, привыкли к доступности средств прямого управления вводом-выводом для любой программы, но в многозадачных ОС о такой доступности для прикладной программы может идти речь только в исключительных случаях, а в многопользовательских ОС она исключается вообще.

Можно в общем случае определить четыре метода, которые могут использоваться ОС для конструирования виртуальных устройств (виртуализации):

Одна и та же ОС может использовать разные методы виртуализации для разных устройств.

Метод закрепления однозначно отображает виртуальное устройство на реальное устройство. Метод закрепления наименее эффективен, так как закрепляемое устройство является монопольно используемым ресурсом и применение этого метода порождает все проблемы, связанные с использованием таких ресурсов.

Метод разделения применим к устройством, ресурс которых является делимым. В этом случае ресурс устройства разбивается на части, каждая из которых закрепляется за одним процессом. Примером применения метода могут служить минидиски в ОС VM/370 [19]: все пространство диска разбивается на участки, каждый из которых выглядит для процесса как отдельный том. Можно, например, разделять между процессами и экран видеотерминала. Зафиксированная часть устройства является также монопольным ресурсом, и разделение лишь частично снимает остроту проблем управления таким ресурсом. Возможность дробления устройства предполагает внутреннюю адресацию в устройстве (адрес на диске, адрес в видеопамяти). По аналогии с адресацией в памяти, процесс и здесь работает с виртуальными адресами в виртуальном устройстве, а ОС транслирует их в реальные адреса в реальном устройстве.

Метод спулинга заставляет процесс обмениваться данными не с реальным устройством, а с некоторой буферной областью в памяти (оперативной или внешней). Обмен же данными между буфером и реальным устройством организует сама ОС, причем, как правило, с упреждением (при вводе) или с запаздыванием (при выводе). Буферизация прозрачна для процессов и может создавать у них иллюзию одновременного использования устройства - если каждому процессу выделен свой буфер. Примером могут служить спулинг печати, применяемый во всех современных ОС.

Метод моделирования не связан с реальными устройствами вообще. Устройство моделируется ОС чисто программными методами. Естественным применением этого метода является отработка приемов работы с устройствами, отсутствующими в конфигурации данной вычислительной системы. Часто ОС удобно представлять некоторые свои ресурсы как метафоры (подобия) устройств - это также моделируемые устройства. Так, в VM/ESA обмен данными между виртуальными машинами ведется через виртуальный адаптер, метафорой устройства можно также считать межпрограммный канал (pipe), реализованный во многих современных ОС.

При любом методе виртуализации ОС является "прослойкой" между процессами и реальными устройствами. Эту функцию выполняют входящие в состав ОС драйверы устройств. К драйверам обращаются и другие модули ОС, и процессы пользователя, причем последние, как правило, не непосредственно, а через библиотеки вызовов, предоставляющие более удобный API. В некоторых случаях ОС может предоставить пользователю интерфейс, обладающий высокой степенью подобия с интерфейсом реального устройства, но и в этом случае ОС, даже применяя метод выделения, производит обработку управляющих воздействий, сформированных процессом: проверку правильности команд, трансляцию адресов памяти, адресов устройств и адресов в устройствах и т.п.

6.2. Интерфейсы устройств

Здесь мы рассматриваем взаимодействия ОС с устройствами, лежащие на уровне "интерфейса оборудования" - вспомните рис.1.2.

При всем многообразии внешних устройств ЭВМ и способов управления ими их программные интерфейсы могут быть сведены к трем основным моделям, определяющимся способом подключения устройств к ЭВМ:

Устройство может быть подключено к процессору через регистры устройства, как показано на Рисунке 6.1. Такое подключение применяется для устройств, которые имеют простое управление, и обмен с ними ведется небольшими порциями данных (байт, слово, двойное слово). Устройство может иметь большое число регистров, которые, однако, сводятся к трем основным типам: регистры состояния - для передачи в процессор информации о состоянии, регистры управления - для передачи на устройство команд, регистры данных - для обмена данными между процессором и устройством. Регистры управления и состояния, как правило, являются однонаправленными, регистры данных могут быть как одно-, так и двунаправленными. Регистры устройств являются расширением адресного пространства ЭВМ. Расширение это может быть как явным - с доступом при помощи команд работы с памятью типа MOV, так и неявным - с отдельной адресацией портов ввода-вывода и доступом при помощи специальных команд типа IN/OUT.


Рис.6.1. Прямое подключение устройства

Сколько-нибудь сложные по управлению устройства подсоединяются к ЭВМ через контроллеры ввода-вывода (устройства управления), причем один контроллер может обслуживать несколько однотипных устройств, как показано на Рисунке 6.2. С точки зрения программного интерфейса это подключение ничем не отличается от предыдущего варианта, регистры контроллера выглядят для программы так же, как и регистры устройств.


Рис.6.2. Подключение через контроллер

Быстродействие устройств много ниже быстродействия центрального процессора, поэтому обычно после выдачи команды на устройство программа должна дожидаться ее завершения. Программа может убедиться в завершении операции одним из двух способов: опросом или прерыванием. Опрос предполагает периодическое чтение регистра состояния устройства и проверку в нем признака завершения операции. Крайним случаем опроса является занятое ожидание - когда программа опрашивает устройство практически непрерывно, ничем другим не занимаясь. Помимо того, что при этом непроизводительно расходуется процессорное время, занятое ожидание еще и небезопасно: при отсутствии сигнала окончания от устройства (например, при сбое последнего) программа может "зависнуть" в состоянии занятого ожидания, а если она при этом была в непрерываемом состоянии - заблокировать работу всей системы.

Более действенным способом сигнализации об окончании операции является прерывание от устройства. При большом разнообразии механизмов прерываний в разных архитектурах вычислительных систем все они обеспечивают сохранение вектора состояния прерванного процесса и идентификацию устройства, приславшего прерывание. При использовании прерываний ОС после выдачи команды ввода-вывода на устройство переводит процесс в состояние ожидания. Прерывание, присланное устройством, обрабатывается ядром ОС, которое при этом разблокирует процесс, ожидающий завершения операции.

Для устройств, с которыми обмен ведется большими порциями информации, применяется прямой доступ к памяти (ПДП), показанный на Рисунке 6.3. Контроллер ПДП работает параллельно с центральным процессором и обменивается данными прямо с оперативной памятью, минуя центральный процессор. Сам контроллер ПДП выглядит для программы как устройство с доступом через регистры. Программа должна его запрограммировать, записав в его регистры адрес области оперативной памяти, с которой происходит обмен, и размер блока данных, а затем запустить операцию, которая инициирует прямой обмен. Регистр данных контроллера ПДП при этом используется только для передачи управляющей информации. Об окончании обмена программа может узнать либо по прерыванию, либо опрашивая регистр состояния контроллера. Контроллер ПДП обычно содержит собственную буферную память для сглаживания разницы в быстродействии устройства и оперативной памяти. Напомним, что аппаратура ПДП обычно не обеспечивает динамическую трансляцию адресов. Поэтому ОС, получив от процесса запрос на выполнение операции ввода-вывода через ПДП, фиксирует в реальной памяти ту часть виртуального адресного пространства программы, с которой происходит обмен - до окончания обмена. Отметим также, что непрерывное виртуальное адресное пространство процесса может отображаться в несмежные страничные кадры реальной памяти, поэтому ОС может водить-выводить непрерывный с точки зрения процесса блок данных за несколько операций обмена.


Рис.6.3. Подключение через ПДП

В сущности, и контроллер обычного устройства, и контроллер ПДП представляют собой специализированные процессорные устройства, но более полно это качество присуще каналу ввода-вывода. Каналы представляют собой специализированные процессоры, имеющие свою систему команд и работающие параллельно с центральным процессором, но использующие ту же оперативную память. В отличие от контроллеров, которые являются специализированными по типам устройств, каналы являются универсальными процессорами ввода-вывода, к одному каналу могут быть одновременно подсоединены контроллеры разных устройств. Работа канала во многом похожа на работу контроллера ПДП: канал программируется, а затем запускается операция, в ходе которой канал обеспечивает прямой обмен с оперативной памятью, минуя центральный процессор. Подключение через канал показано на Рисунке 6.4.


Рис.6.4. Подключение через канал ввода-вывода

Идея канала ввода-вывода, впервые реализованная в System/360 фирмы IBM, была впоследствии воплощена в ряде других архитектур. Ввиду своей продуктивности эта идея без концептуальных изменений была перенесена и в System/370, и в System/390, и отказа от нее в перспективе также не предвидится. В последующем изложении мы опираемся именно на эти реализации каналов ввода-вывода.

Существует несколько типов каналов (селекторный, мультиплексный, байт-мультиплексный), предназначенных для подключения устройств с разными скоростями обмена, но для программиста все они выглядят одинаково. Средства программирования канала, во-первых, гораздо более мощные и гибкие, чем контроллера ПДП, во-вторых, позволяют унифицировать программирование ввода-вывода для различных устройств.

Выполнение операции ввода-вывода подразумевает совместное (параллельное или квазипараллельное) функционирование нескольких "субъектов": основной программы, выполняющейся на центральном процессоре (далее - программа ЦП), канала и устройства (далее - канал), аппаратного механизма прерываний и программы обработки прерываний. Программа ЦП формирует в оперативной памяти программу канала, сообщает системе ввода-вывода ее адрес, назначает Блок управления событием, в котором будет сделана отметка о завершении операции, и выдает команду "Начать ввод-вывод", адресующую канал и устройство. Канал проверяет готовность канала/устройства и начинает выполнение канальной программы. При этом команда "Начать ввод-вывод" завершается, и программа ЦП продолжает свое выполнение. Когда у программы ЦП возникает необходимость дождаться завершения операции, она опрашивает Блок управления событием. Если в нем есть отметка о выполнении, программа продолжает выполняться, в противном случае - переводится в состояние ожидания до появления отметки в Блоке управления событием. Канал при завершении операции сообщает системе ввода-вывода информацию о своем состоянии и инициирует прерывание по вводу-выводу. Аппаратный механизм прерывания сохраняет текущее состояние программы и обеспечивает передачу управления на программу обработки прерываний по вводу-выводу. Программа обработки прерываний распознает канал и устройство, пославшие прерывания, и передает управление на соответствующую ветвь обработки. Из информации о состоянии канала определяется причина прерывания. Выполняются действия по обработке соответствующей ситуации. Диагностическая информация может быть также записана в область памяти, доступную для программы ЦП. Если прерывание, сообщает об окончании операции, обработчик прерывания делает отметку в Блоке управления событием. Обработчик прерывания возвращает управление в прерванную программу. Программа ЦП, если это необходимо, анализирует диагностическую информацию о результатах выполнения операции.

Программа канала размещается в оперативной памяти и представляет собой массив канальных команд. Каждая команда канальной программы - структура данных фиксированной длины, содержащая код операции, адрес области памяти, с которой происходит обмен, код контроля доступа к памяти, признаки режима выполнения, объем передаваемых или принимаемых данных.

Различаются несколько типов операций: "чтение" (передача данных из устройства в память), "запись" (передача данных из памяти в устройство), "управление" (выполнение специфических операций на устройстве, например, перемотка магнитной ленты) - эти команды специфичны для устройств, устройство может иметь несколько модификаций одной команды. Общей для всех устройств является команда "уточнить состояние" - передача в память информации о состоянии устройства. Команда "переход в канале" на устройство не передается, она изменяет последовательность выполнения канальных команд. Код контроля доступа к памяти позволяет предотвратить вторжение процесса в операциях ввода-вывода в области памяти, принадлежащие другим процессам или ОС. Среди признаков режима выполнения наибольший интерес представляют три. Признак "цепочка команд" определяет продолжение программы канала в следующей команде. Признак "цепочка данных" задает выполнение следующей канальной команды как продолжения текущей: поле кода операции в ней игнорируется, но все остальные поля обновляются. Если отсутствуют признаки "цепочка команд" или "цепочка данных", канальная команда считается последней в программе. Признак "программно управляемое прерывание" задает генерацию прерывания при выборке каналом команды, содержащей этот признак, это дает возможность синхронизировать работу программы на центральном процессоре с выполнением канальной программы.

Взаимодействие между субъектами выполнения операции ввода-вывода происходит также через фиксированные адреса памяти, в которые записывается управляющая и диагностическая информация: Адресное слово канала, Слово состояния канала, Слово состояния программы.

Как и контроллер ПДП, канал не выполняет динамическую трансляцию адресов, кроме того, в системах, где процесс работает только в своей виртуальной памяти, не имея дела с реальным адресами, ОС транслирует программу канала, размещая результат трансляции в недоступной для процесса области. В этом случае процесс не может модифицировать программу канала в ходе ее выполнения.

Контроллер ПДП и канал ввода-вывода являются специализированными процессорами. Следующим шагом в интеллектуализации контроллеров ввода-вывода являются процессоры ввода-вывода - универсальные процессоры, выполняющие функции контроллера ввода-вывода. Впервые процессоры ввода-вывода были применены в вычислительной системе CDC-6600, считающейся первым суперкомпьютером. С тех пор процессоры ввода-вывода перестали быть прерогативой суперсистем и постепенно внедряются в системы ординарные (например, AS/400). Интерфейс процессора ввода-вывода не похож на интерфейс устройства в обычном понимании. Взаимодействие ОС с процессором ввода-вывода происходит через механизм обмена сообщениями, поддерживаемый микроядром. Данные могут передаваться как в составе сообщения, так и выбираться процессором ввода-вывода непосредственно из оперативной памяти. Взаимодействие через сообщения, естественно, занимает больше времени, чем управление вводом-выводом через контроллер или канал, но выигрыш в эффективности получается за счет переноса некоторых (иногда весьма значительных по объему) операций обработки данных в процессор ввода-вывода. Являясь универсальным процессором, процессор ввода-вывода работает под управлением своей собственной мини-ОС и программа управления устройством имеет статус приложения в этой ОС.

6.3. Управление устройствами

Внешние устройства ЭВМ отличаются большим разнообразием в форматах управляющей информации и в алгоритмах управления. Даже в канальной модели интерфейса разные устройства имеют разные модификации команд чтения/записи/управления, не говоря уже о том, что транзакция на устройстве обычно подразумевает выполнение нескольких команд, и последовательности их бывают совершенно различными для разных устройств.

Модули ОС, которые осуществляют трансляцию однотипных для всех устройств обращений к ним из процессов и из других модулей ОС в специфические для устройства управляющие воздействия и управляют выполнением этих воздействий, называются драйверами. (Мы не согласны с теми авторами, которые называют драйверами любые программы управления вводом-выводом, драйверы в нашем понимании обязательно включаются в состав ОС и обязательно соответствуют спецификациям данной ОС.) Каждому типу устройства соответствует свой драйвер. Драйвер устройства имеет два основных уровня, как показано на рис.6.5. Первый (верхний) уровень принимает системные вызовы от процессов и формирует на основании каждого вызова запрос. Этот же уровень выстраивает запросы в очередь и поддерживает упорядоченность этой очереди в соответствии с принятой дисциплиной обслуживания. Второй (нижний) уровень драйвера выбирает из очереди первый запрос и обслуживает его: формирует управляющие воздействия и передает их на устройство, обрабатывает прерывания от устройства и сообщает ядру ОС о наступлении событий, связанных с вводом-выводом.


Рис. 6.5. Структура драйвера

Как мы сказали, верхний уровень драйвера определяет очередность, в которой обслуживаются запросы от разных процессов. Для реализации политики обслуживания драйвер должен учитывать как приоритеты процессов, так и доступность устройств. Приоритетность запросов может оцениваться по разным стратегиям, из которых наиболее распространенной является та, согласно которой запрос процесса, имеющего наивысший процессорный приоритет, имеет наивысший приоритет в очереди драйвера и другие стратегии, ставящие целью повышение эффективности обмена с устройством, пример такой стратегии мы рассматриваем ниже применительно к драйверам дисковых накопителей.

Доступность устройства мы рассмотрим на примере канальной модели подключения. Путь от процессора к устройству включает в себя три "станции": канал, контроллер, устройство. Каждая из станций пути может быть свободна или занята независимо от других. На приведенной на рис. 6.4 древовидной структуре различные уровни этой структуры характеризуются различным временем своего участия в операции ввода-вывода. Канал участвует только в передаче данных. Например, при выводе канал может быть занятым только на время передачи из памяти в буфер контроллера, после чего канал освобождается и может обслуживать другой контроллер, а первый контроллер тем временем передает данные на устройство. После передачи данных на устройство освобождается контроллер, а устройству может потребоваться еще некоторое время, чтобы обработать полученные данные. Поэтому события освобождения канала, контроллера и устройства индицируются разными признаками в состоянии канала. В наборе команд ввода-вывода есть отдельные команды для проверки состояния устройства, контроллера и канала. Адрес устройства в схеме подключения, подобной той, что представлена на рис. 6.4, должен складываться из: идентификатора (номера) канала, идентификатора контроллера в канале, идентификатора устройства в контроллере. Процесс обращается к устройству по некоторому своему идентификатору - виртуальному адресу, который может быть подобен реальному, а может представлять собой и логическое имя устройства. В простейшем случае трансляция адреса устройства производится по таблице перекодировки. Возможна и более сложная структура, допускающая подключение устройства к нескольким контроллерам, а контроллера - к нескольким каналам. Реальный адрес устройства может формироваться, таким образом, динамически. В IBM System/390 эти функции переданы аппаратной подсистеме ввода-вывода.

Для принятия решений о доступности устройств ОС поддерживает таблицы дескрипторов, отражающие состояние станций пути (три таблицы - по числу типов станций). Для канала дескриптор включает в себя: идентификатор канала; состояние (занят/свободен); список контроллеров, подключенных к каналу; список запросов к каналу. Для контроллера: идентификатор контроллера; состояние; список каналов, к которым подключен контроллер; список устройств, подключенных к контроллеру; список запросов к контроллеру. Для устройства: идентификатор устройства; состояние; список контроллеров, к которым подключено устройство; список запросов к устройству.

Логически являясь частью ОС, драйверы, тем не менее оформляются как отдельные модули. Поскольку каждый драйвер однозначно связан с устройством определенного типа (а возможно, и данной модификации), то и состав набора драйверов зависит от конфигурации аппаратных средств. Кроме того, обязательно должна быть обеспечена возможность подключения к системе новых внешних устройств без внесения изменений в ОС. При модульности драйверов это достигается простым добавлением нового драйвера к системному программному обеспечению. Драйверы загружаются в память либо при загрузке системы, либо (реже) - динамически, при возникновении потребности в них. Выбор драйверов для загрузки выполняется либо по явным указаниям в процедуре инициализации ОС, либо неявно - по имеющимся таблицам конфигурации системы, либо полностью автоматически - путем опроса при загрузке всех установленных устройств, опознания их и подключения соответствующих драйверов.

6.4 Примеры драйверов устройств

Приводимые ниже примеры показывают, что на драйверы некоторых устройств часто возлагаются дополнительные функции помимо непосредственного управления вводом-выводом.

Драйвер системных часов.

Вычислительные системы имеют один или два таймера. Обязательным является линейный таймер, генерирующий прерывания центрального процессора через фиксированные интервалы времени. Возможен также работающий независимо от линейного программируемый таймер, который генерирует однократное прерывание через заданный интервал времени от момента задания. Прерывания такого таймера иногда называются сигналом тревоги. Если программируемый таймер отсутствует, он может моделироваться при помощи интервального таймера и программных средств.

Драйвер линейного таймера осуществляет обработку его прерываний и в типовом случае может выполнять следующие действия по каждому прерыванию:

Драйвер клавиатуры.

Этот драйвер предназначен для ввода символов с клавиатуры терминала. В большинстве аппаратных архитектур нажатие любой клавиши на клавиатуре вызывает прерывание. Обработчик этого прерывания в типовом случае выполняет:

Большинство драйверов позволяют пользователю терминала производить упреждающий ввод данных - до того, как на них поступит запрос из программы. Введенные данные становятся доступными для чтения при нажатии клавиши ввода. Код этой клавиши сохраняется в буфере как признак конца строки. При поступлении запроса на чтение данных с клавиатуры драйвер выбирает из буфера строку - до признака конца строки. Если этот признак отсутствует, то процесс, выдавший запрос, блокируется до появления законченной строки в буфере драйвера. Некоторые драйверы запоминают введенные строки в стеке и обрабатывают также специальные клавиши, позволяющие выбирать строки из стека.

Драйверы дисковых запоминающих устройств.

Обычной функцией такого драйвера является перевод виртуального адреса на диске в реальный (физический). Физический адрес на диске состоит из трех компонент: головка, дорожка, сектор (в дисковых архитектурах без разбиения на сектора - смещение на дорожке). Драйвер же формирует для процессов виртуальный диск, представляемый как линейная последовательность секторов, виртуальным адресом является номер сектора.

Интересной функцией дискового драйвера может быть планирование запросов на ввод-вывод с целью повышения эффективности обмена. В соответствии со структурой физического адреса доступ к данным на диске состоит из трех этапов - выборок составляющих этого адреса: выбора головки, выбора дорожки и выбора сектора. Выбор головки чтения/записи производится простым электрическом переключением практически мгновенно. Выбор дорожки - самый времяемкий этап: он требует механического перемещения головок к требуемой дорожке; время этого перемещения зависит от расстояния перемещения. Выбор сектора на дорожке требует ожидания момента, когда требуемый сектор окажется под головкой (за счет вращения диска), время выбора сектора много меньше времени выбора дорожки.

Драйвер упорядочивает очередь запросов таким образом, чтобы минимизировать среднее время поиска дорожки. Обсуждение стратегий обслуживания мы далее ведем, исходя из предположения о случайном распределении запросов по пространству диска. Обслуживание очереди по дисциплине FCFS, очевидно, приведет к хаотическому перемещению головок и в результате - к невысокой пропускной способности драйвера и значительным механическим нагрузкам на дисковод. Из дисциплин обслуживания, позволяющих повысить пропускную способность, наиболее известными являются следующие:

Авторы, приводящие результаты исследования функционирования этих стратегий на моделях [8, 27], рекомендуют стратегию Scan при малых нагрузках и C-Scan - при больших.

Совместно с любым методом сокращения времени выбора дорожки может применяться алгоритм минимизации задержки от вращения диска SLTF (shortest latency time next - с наименьшим временем задержки - первый): при наличии нескольких запросов к одной дорожке они упорядочиваются таким образом, чтобы все они могли быть обслужены за один оборот диска.

Другим примером функций, возлагаемых на драйвер дисков, может быть поддержка RAID-технологий - использование избыточных дисковых устройств для обеспечения возможности восстановления данных при сбоях. В настоящее время имеется широкий спектр реализаций RAID-технологий - от полного переноса их на аппаратуру ввода-вывода до полной реализации их в драйвере.

6.5 Потоки и многоуровневые драйверы.

В целом ряде случаев данные, передаваемые из процесса на устройство или в обратном направлении, должны быть дополнительно преобразованы. Такими преобразованиями могут быть: кеширование данных, сжатие данных, криптографическое кодирование данных, согласование с форматами сетевых протоколов и т.д.

Выполнение этих функций может быть заложено непосредственно в драйвер устройства. Однако, такой вариант снижает мобильность драйвера, так как не во всех применениях данной вычислительной системы и данной ОС эти функции могут быть необходимы, требования к этим функциям могут меняться, что повлечет за собой необходимость создания нового драйвера. Даже для разных процессов могут понадобиться разные способы модификации данных. Решение этой проблемы содержится в идее потоков.


Рис.6.6. Поток ввода-вывода

Поток (stream) представляет собой цепочку очередей, через которые проходят данные, передаваемые от процесса драйверу устройства или в обратном направлении. Каждая очередь в этой цепочке обрабатывается одним программных модулем - модулем потока, как показано на Рисунке 6.6. Данные движутся по цепочке очередей от процесса к драйверу или обратно, по пути проходя обработку в модулях потока. Модуль потока выбирает данные из своей очереди, выполняет их обработку и передает данные в очередь к следующему модулю потока. Когда модуль завершает обработку очередной порцию информации, он запускает на выполнение следующий модуль, который обрабатывает эти данные или заносит их в свою очередь. Вставляя в поток новые модули (и, соответственно, новые очереди) можно обеспечивать сколь угодно сложную дополнительную обработку данных.

Потоки прозрачны для пользовательских процессов, но последним предоставляется возможность вставлять в потоки собственные модули.

Поток может быть однонаправленным или дуплексным, то есть, содержать две параллельные цепочки очередей - вводную и выводную, как и показано на рис 6.6.

Развитием идеи потоков являются многоуровневая структура драйверов, которая применяется в ряде современных ОС. Обработка данных проходит в этом случае через цепочку отдельных драйверов. Места драйверов в цепочке распределяются таким образом, чтобы каждый драйвер более низкого уровня осуществлял обработку, более привязанную к конкретному устройству. Драйверы высоких уровней могут обслуживать несколько устройств одного типа, но разных моделей. Непосредственно аппаратно-зависимым является только драйвер самого нижнего уровня (аппаратный драйвер). В некоторых ОС даже аппаратный драйвер не осуществляет прямого взаимодействия с устройством, а использует для этого сервис ОС. В многоуровневой структуре драйверов может быть оставлено место и для пользовательских драйверов, выполняющих специфическую обработку данных.

6.6 Интерфейс процесса

Как мы уже отметили выше, ОС конструирует для процессов виртуальные устройства высокого уровня. Характерным примером таких устройств являются файлы. Процесс может работать с каждым файлом, как с отдельным устройством, хотя на самом деле файл представляет собой лишь часть дискового пространства. В ОС Unix концепция файла была впервые введена как универсальная метафора устройства. Это позволяет свести все многообразие управления вводом-выводом к единой форме системных вызовов. Ниже приведен набор системных вызовов для работы с устройствами, являющийся практически общепринятым.

Открыть устройство:

    devHandle = open(devNname, mode) 

Этот вызов сообщает драйверу, что процесс будет работать с данным устройством. Вызов является частным случаем вызова getResource, и при его выполнении могут производиться действия по предупреждению или обнаружению тупиков. Процесс может быть заблокирован, если требуемое устройство занято. Могут проверяться права доступа данного процесса к данному устройству. На устройстве могут выполняться какие-то специфические для устройства действия - начальные установки. Параметр mode определяет направление обмена данными с устройством: чтение, запись, чтение/запись, запись в конец. Никакие другие операции с устройством невозможны, если для него не выполнена операция открытия. Манипулятор (handle), возвращаемый системным вызовом open, служит идентификатором открытого устройства во всех последующих операциях с ним.

Закрыть устройство:

    close(devHandle) 

Вызов сообщает драйверу, что процесс закончил работу с устройством. Ресурс-устройство открепляется от процесса, разблокируются другие процессы, ожидающие освобождения этого устройства. На устройстве могут выполняться специфические заключительные операции.

Читать данные из устройства в память:

    read(devHandle, vAddr, counter) 

Это запрос на передачу данных из устройства, идентифицируемого манипулятором devHandle, в задаваемую виртуальным адресом vAddr область адресного пространства процесса. Если на устройстве нет того объема информации, который задан счетчиком counter, может быть передан меньший объем. Вызов обычно возвращает действительный объем переданной информации.

Писать данные из памяти в устройство:

    write(devHandle, vAddr, counter) 

Запрос на передачу данных из заданной области виртуальной адресного пространства процесса на устройство.

Позиционировать устройство:

    seek(devHandle, position) 

Запрос на установку устройства в заданную позицию. Применяется для устройств, имеющих внутреннюю адресацию. По умолчанию вызовы read и write устанавливают устройство в позицию, следующую за прочитанными или записанными данными.

Управление вводом-выводом:

    ioctl(devHandle, command, parameters) 

Запрос на выполнение специфических для устройства операций, не вписывающихся в перечисленные выше системные вызовы. Операция определяется командой command, например: чтение, запись, управление и т.п. Третий параметр задает дополнительные параметры, специфичные для каждой операции.

При реализации системных вызовов read и write перед разработчиком ОС возникает вопрос: блокировать или не блокировать процесс, выдавший системный вызов? Возможна синхронная и асинхронная организация ввода-вывода. При синхронной организации процесс, выдавший запрос на чтение/запись данных, требующий физического обмена данными с устройством, безусловно блокируется. Когда процесс возобновляется, он может быть уверен, что данные, которые он запросил (read), уже находятся в его рабочей области, и он может их использовать, а данные, которые он передал (write), уже пересланы, и он может записывать в область, в которой они находились, другую информацию. Альтернативной является асинхронная организация ввода-вывода, при которой системный вызов read/write только инициирует операцию, далее процесс продолжает выполняться параллельно со вводом-выводом. Параллельное выполнение позволяет повысить эффективность работы как самого процесса, так и всей системы, так как снимает необходимость в переключении процессов, но синхронизация все равно необходима. Если, например, процесс запросил ввод данных, то прежде, чем он начнет их использовать, он должен убедиться, что ввод завершился. Ответственность за такую синхронизацию перекладывается на процесс. Для предоставления процессу возможности синхронизировать свои действия с выполнением операций ввода-вывода ОС обеспечивает процессу виртуальные прерывания, поддерживаемые системными вызовами, описываемыми далее.

Ждать завершения операции на устройстве:

    wait(devHandle, delay) 

Вызов блокирует процесс - переводит его в состояние ожидания до тех пор, пока не поступит виртуальное прерывание, сигнализирующее о завершении операции на устройстве, определяемом devHandle. Если операция к моменту вызова уже завершилась, процесс не блокируется. Параметр delay задает максимально допустимое время, которое процесс может ожидать. Если это время выходит, системный вызов wait заканчивается, возвращая признак ошибки. Это время может быть установлено и бесконечно большим.

Установить обработчик виртуального прерывания от устройства:

    setHandler(devHandle, procAddr) 

Возможность, предоставляемая этим вызовом, обеспечивает полное сходство виртуального прерывания с реальным. При поступлении виртуального прерывания от устройства выполнение процесса прерывается и управление передается на процедуру с адресом procAddr, которую процесс назначил обработчиком виртуального прерывания. Все обработчики виртуальных прерываний должны соответствовать несложным системным спецификациям. Обычно обработчик имеет параметры, через которые ему передается дополнительная информация о виртуальном прерывании. Еще раз подчеркнем, что речь здесь идет именно о виртуальных, а не о реальных прерываниях. Виртуальное прерывание обеспечивается не аппаратными средствами, а моделируется для процесса операционной системой.

Асинхронная организация ввода-вывода предоставляет пользователю больше возможностей для повышения эффективности выполнения процесса, но вместе с тем, и больший простор для ошибок. Характерной, например, может быть такая ошибка, появление которой должно быть предусмотрено в ОС. При записи данных, например, правильной является такая последовательность системных вызовов:

    write...wait...write...wait... , 

то есть, следующий блок информации не выводится, пока не будет закончен вывод предыдущего. Что произойдет, если последовательность вызовов будет такой:

    write...write...wait...wait... ? 

Если ОС связывает с операцией ввода вывода двоичный флаг, то первый вызов write установит этот флаг в состояние "занято", второй не изменит значение флага. Окончание первой операции вывода сбросит флаг в "свободно", что будет обработано первым вызовом wait, окончание второй операции вывода не повлияет на состояние флага, и второй вызов wait в любом случае найдет флаг в состоянии "свободно". Момент окончания второй операции, таким образом, будет утерян. ОС должна либо расценивать подобную последовательность вызовов как ошибку процесса, либо поддерживать ее, создавая для каждой операции свой флаг.

6.7 Буферизация

Хотя блокировки и не влияют на результат выполнения процесса и не отражаются на его виртуальном времени, они сказываются на реальном времени его выполнения. Для интерактивных процессов они могут стать основным фактором, определяющим время реакции процесса. Невыгодны блокировки и для ОС, так как каждая блокировка - это переключение процессов, а следовательно, накладные расходы. Одним из способов, позволяющим избежать блокировок (или, по крайней мере, уменьшить их количество), является буферизация данных. Для устройства ввода-вывода назначается буферная область в оперативной памяти. Обмен данными происходит между процессом и буферной областью, а обмен между буферной областью и устройством выполняет ОС независимо от выполнения процесса. Если, например, выполнение системного вызова write будет включать в себя только пересылку данных в оперативной памяти, то блокировка процесса на время выполнения этого вызова не нужна. Буферизация, таким образом, сглаживает различия в скоростях работы производителя и потребителя информации и позволяет избежать излишних блокировок.

Особенно существенный эффект буферизация может дать при последовательном вводе-выводе, так как при вводе ОС может предсказать, какой блок информации понадобится следующим и произвести его упреждающее чтение в буфер. Еще один полезный эффект буферизации - ОС имеет возможность сгруппировать данные в буфере и производить обмен с внешним устройством большими блоками.

Богатые вариантами средства организации ввода-вывода с использованием буферизации были впервые реализованы в OS/360[15] и унаследованы следующими поколениями ОС мейнфреймов. При синхронной организации ввода-вывода (в OS/360 - "методы доступа с очередями") буферизация прозрачна для процесса, она полностью обеспечивается ОС. При асинхронной организации ("базисные методы доступа") процесс сам может организовать буферизацию. Устройству назначается буферная область, которая форматируется как буферный пул. В методах с очередями это назначение происходит автоматически, программист может только изменить размер пула, если его не устраивает принятый по умолчанию. В базисных методах есть специальные системные вызовы для выделения, освобождения, форматирования буферного пула и связывания пула с устройством. Буферизация не обязательно означает дополнительную пересылку данных в оперативной памяти. Методы с очередями предоставляют три альтернативных режима управления буферизацией:

Два интересных примера буферизации мы возьмем из ОС Unix.

В Unix буферизация обмена с дисковыми накопителями (кэширование) является тотальной, через кэш проходят все данные, которыми ОС и процессы обмениваются с дисками, и под кэш отводится значительная часть оперативной памяти. Кэширование дает значительный эффект и при случайном (непоследовательном) обмене, характерном для многозадачной многопользовательской ОС. В дескрипторе каждого буфера в кеше имеются поля:

Список свободных буферов организован как простая FIFO-очередь: ядро ОС выбирает буфер из головы списка, освобождающийся буфер включает в конец списка. Таким образом, буфер, попавший в список свободных еще некоторое время находится в этом списке, сохраняя свое содержимое, и может быть выбран из списка, если к нему будет обращение. При обращении к дисковому вводу-выводу основным параметром является адрес блока на внешней памяти. ОС прежде всего ищет в кеше соответствующий блоку буфер и только при неудачном поиске в кеше назначает свободный буфер и производит физическое обращение к диску. Для ускорения поиска в кеше все буфера распределяются по нескольким спискам, именуемым хеш-очередями. Номер списка определяется как результат весьма простой функции хеширования, аргументом которой является адрес на внешней памяти. Применение хеширования позволяет равномерно распределить буферы по хеш-очередям. Каждый буфер может входить только в одну хеш-очередь, но также может быть включен и в список свободных. Процесс, выдавший запрос на ввод-вывод, блокируется в случаях:

При освобождении блока разблокируются все процессы, ждущие освобождения этого или любого блока.

Все современные ОС (OS/2, Windows 95, Windows NT) в той или иной степени применяют тотальное кэширование при обмене с дисками.

Второй пример - буферизация ввода в драйвере терминала. Задача такой буферизации - обеспечить накопление данных и возможность упреждающего ввода с терминала. Для этой буферизации характерны: последовательный ввод, сравнительно небольшие объемы данных в буфере, непостоянный объем информации, содержащейся в буфере. Буфер представляет собой цепочку блоков, каждый из которых имеет постоянную длину N. В каждом блоке имеются следующие поля:


Рис.6.7. Буфер терминала для Unix

Пример буфера при N=8 показан на Рисунке 6.7. Ядро ОС хранит указатели на первый и последний блоки цепочки и ведет список свободных блоков (очередь LIFO). Ядро обеспечивает:

Контрольные вопросы

  1. Охарактеризуйте методы виртуализации устройств в ОС. Приведите примеры их применения.
  2. Почему даже при закреплении устройства за процессом устройство все равно остается виртуальным?
  3. Чем Вы объясните столь долгое и успешное существование концепции каналов ввода-вывода?
  4. Чем объясняется двухуровневая (как минимум) структура драйвера устройства?
  5. В некоторых современных ОС драйверами называются также и модули ОС, не имеющие отношения к управлению устройствами. Чем может быть объяснен такой подход?
  6. Назовите те функции, которые Вы считаете целесообразным добавить в драйвер клавиатуры для обеспечения большего удобства пользователю.
  7. Каким образом можно обеспечить выдачу процессу "сигнала тревоги" через заданный интервал времени, если в системе нет программируемого интервального таймера?
  8. В чем цель стратегии драйвера диска?
  9. Сопоставьте потоки и многоуровневые драйверы. В чем их сходство и различия?
  10. Какие цели преследует буферизация ввода-вывода?
  11. Сопоставьте по эффективности три режима буферизации (пересылка, указание, подстановка), описанные в разделе 6.7.
  12. Для чего нужен системный вызов ioctl? Приведите примеры устройств, для которых этот вызов совершенно необходим.

НазадОглавлениеВперед
Индекс