Операционные системы. Управление ресурсами

       

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


Как мы уже отметили выше, ОС конструирует для процессов виртуальные устройства высокого уровня. Характерным примером таких устройств являются файлы. Процесс может работать с каждым файлом, как с отдельным устройством, хотя на самом деле файл представляет собой лишь часть дискового пространства. В ОС 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 в любом случае найдет флаг в состоянии "свободно". Момент окончания второй операции, таким образом, будет утерян. ОС должна либо расценивать подобную последовательность вызовов как ошибку процесса, либо поддерживать ее, создавая для каждой операции свой флаг.




Содержание раздела