Управление ресурсами в автономных
Однопроцессорных компьютерах
Управление памятью
Память является важнейшим ресурсом, требующим тщательного управления со стороны операционной системы. Распределению подлежит вся оперативная память, не занятая операционной системой. Обычно ОС располагается в самых младших адресах, однако может занимать и старшие адреса. Функциями ОС по управлению памятью являются: отслеживание свободной и занятой памяти, выделение памяти процессам и освобождение памяти при завершении процессов, вытеснение процессов из оперативной памяти на диск (когда размеры основной памяти не достаточны для размещения в ней всех процессов) и возвращение их в оперативную память (когда в ней освобождается место), а также настройка адресов программы на конкретную область физической памяти.
Для идентификации команд и переменных используются символьные имена (метки), виртуальные адреса и физические адреса.
Символьные имена присваивает пользователь при написании программы на алгоритмическом языке или ассемблере.
Виртуальные адреса вырабатывает транслятор, переводящий программу на машинный язык. Так как во время трансляции в общем случае неизвестно, в какое место оперативной памяти будет загружен процесс, то транслятор присваивает командам и переменным виртуальные (условные) адреса, обычно считая по умолчанию, что процесс будет размещен, начиная с нулевого адреса. Совокупность виртуальных адресов процесса называется виртуальным адресным пространством. Каждый процесс имеет собственное виртуальное адресное пространство. Максимальный размер виртуального адресного пространства ограничивается разрядностью адреса, присущей данной архитектуре ВМ, и, как правило, не совпадает с объемом физической памяти, имеющимся в машине.
Физические адреса соответствуют номерам ячеек оперативной памяти, где в действительности расположены или будут расположены команды и переменные. Переход от виртуальных адресов к физическим может осуществляться двумя способами.
В первом случае замену виртуальных адресов на физические делает специальная системная программа – перемещающий загрузчик. Перемещающий загрузчик на основании имеющихся у него исходных данных о начальном адресе физической памяти, в которую предстоит загружать процесс, и предоставленной транслятором информации об адресно-зависимых константах программы, выполняет загрузку процесса, совмещая ее с заменой виртуальных адресов физическими.
Второй способ заключается в том, что процесс загружается в память в неизмененном виде в виртуальных адресах, при этом операционная система фиксирует смещение действительного расположения программного кода относительно виртуального адресного пространства. Во время выполнения процесса при каждом обращении к оперативной памяти выполняется преобразование виртуального адреса в физический. Второй способ является более гибким, он допускает перемещение процесса во время выполнения, в то время как перемещающий загрузчик жестко привязывает процесс к первоначально выделенному участку памяти. Вместе с тем использование перемещающего загрузчика уменьшает накладные расходы, так как преобразование каждого виртуального адреса происходит только один раз во время загрузки, а во втором случае – каждый раз при обращении по данному адресу.
В некоторых случаях (обычно в специализированных системах), когда заранее точно известно, в какой области оперативной памяти будет выполняться процесс, транслятор выдает исполняемый код сразу в физических адресах.
Все методы управления памятью могут быть разделены на два класса: методы, которые используют перемещение процессов между оперативной памятью и диском, и методы, которые не делают этого. Начнем с последнего, более простого класса методов.
Самым простым способом управления оперативной памятью является разделение ее на несколько разделов фиксированной величины. Это может быть выполнено вручную администратором во время старта системы или во время ее генерации. Очередной процесс, поступивший на выполнение, помещается либо в общую очередь, либо в очередь к некоторому разделу. Подсистема управления памятью в этом случае выполняет следующие задачи:
1) выбирает свободный раздел, размер которого достаточен для размещения поступившего на выполнение процесса;
2) осуществляет загрузку процесса и настройку адресов.
При очевидном преимуществе – простоте реализации – данный метод имеет существенный недостаток, которым является его «жесткость». Так как в каждом разделе может размещаться только один процесс, то степень «многозадачности» заранее ограничена числом разделов не зависимо от того, какой размер требуется процессам. Другим существенным недостатком является то, что в предлагаемой схеме очень часто может наблюдаться так называемая фрагментация памяти, то есть потери части памяти разделов вследствие того, что процессы не полностью занимают выделенные им разделы. Например, если процессу требуется небольшой объем, но ему выделяется целый раздел, то оставшаяся память этого раздела не будет использоваться (то есть в этом разделе останется неиспользуемый фрагмент памяти). Снижение же степени фрагментации памяти за счет уменьшения размеров разделов может на определенном этапе привести к тому, что в малые разделы не смогут быть загружены процессы, для которых требуются более объемные разделы. Кроме того, недостатком рассматриваемого способа управления оперативной памятью является и то, что если даже весь объем оперативной памяти машины позволяет разместить некоторый процесс, требующий большого объема, наличие фиксированного разбиения памяти на разделы не позволит сделать это, так как требуемый объем может превосходить по размеру отдельные (пусть и очень большие), но фиксированные разделы памяти.
В случае распределения памяти разделами переменной величины память ВМ не делится заранее на разделы. Сначала вся оперативная память свободна. Каждому вновь поступившему процессу выделяется необходимая память. Если достаточный объем памяти отсутствует, то процесс не принимается на выполнение и стоит в очереди. После завершения процесса память освобождается, и на это место может быть загружен другой процесс. Таким образом, в произвольный момент времени оперативная память представляет собой случайную последовательность занятых и свободных участков (разделов) произвольного размера.
Задачами операционной системы при реализации данного метода управления памятью являются следующие:
1) ведение таблиц свободных и занятых областей, в которых указываются начальные адреса и размеры участков памяти;
2) при поступлении нового процесса – анализ запроса, просмотр таблицы свободных областей и выбор раздела, размер которого достаточен для размещения поступившего процесса;
3) загрузка процесса в выделенный раздел и корректировка таблиц свободных и занятых областей;
4) после завершения процесса – корректировка таблиц свободных и занятых областей.
Выбор раздела для вновь поступившего процесса может осуществляться по разным правилам, таким, например, как «первый попавшийся раздел достаточного размера», или «раздел, имеющий наименьший достаточный размер», или «раздел, имеющий наибольший достаточный размер». Все эти правила имеют свои преимущества и недостатки.
По сравнению с методом распределения памяти фиксированными разделами метод распределения памяти разделами переменной величины обладает гораздо большей гибкостью, но ему также присуща фрагментация памяти. Это связано с образованием через определенный промежуток времени функционирования системы некоторого числа несмежных участков свободной памяти (фрагментов), образовавшихся в результате выгрузки выполнившихся процессов, и загрузки на их место новых процессов, требующих меньшие объемы памяти. При этом может возникнуть ситуация, при которой ни для одного из вновь поступающих процессов не будет иметься достаточного участка памяти, хотя суммарный объем фрагментов может составить значительную величину, намного превышающую требуемый процессу объем памяти.
Одним из методов борьбы с фрагментацией является перемещение всех занятых участков в сторону старших либо в сторону младших адресов, так, чтобы вся свободная память образовывала единую свободную область. В дополнение к функциям, которые выполняет ОС при распределении памяти переменными разделами, в данном случае она должна еще время от времени копировать содержимое разделов из одного места памяти в другое, корректируя таблицы свободных и занятых областей. Эта процедура называется «сжатием». Сжатие может выполняться либо при каждом завершении процесса, либо только тогда, когда для вновь поступившего процесса нет свободного раздела достаточного размера. В первом случае требуется меньше вычислительной работы при корректировке таблиц, а во втором – реже выполняется процедура сжатия. Так как процессы перемещаются по оперативной памяти в ходе своего выполнения, то преобразование адресов из виртуальной формы в физическую должно выполняться динамическим способом. Хотя процедура сжатия и приводит к более эффективному использованию памяти, она может потребовать значительного времени, что часто является тем недостатком, который преобладает над преимуществами данного метода.
Развитие методов организации вычислительного процесса привело к появлению метода, использующего так называемую виртуальная память. В общем случае виртуальным называется ресурс, который пользователю или пользовательской программе представляется обладающим некими свойствами, которыми он в действительности не обладает. Так, например, пользователю может быть предоставлена виртуальная оперативная память, размер которой превосходит всю имеющуюся в системе реальную оперативную память. Пользователь пишет программы так, как будто в его распоряжении имеется однородная оперативная память большого объема, но в действительности для реализации программы предоставляются несколько разнородных запоминающих устройств, включая дисковые устройства.
Таким образом, виртуальная память – это совокупность программно-аппаратных средств, позволяющих пользователям писать программы, которые для своей реализации требуют такие объемы памяти, которые превосходят реально существующие объемы оперативной памяти ВМ. Для этого виртуальная память решает следующие задачи:
1) размещает процессы в запоминающих устройствах разного типа, например, часть – в оперативной памяти, а часть – на диске;
2) перемещает процессы по мере необходимости между запоминающими устройствами разного типа, например, подгружает процесс или его часть с диска в оперативную память;
3) преобразует виртуальные адреса в физические.
Все эти действия выполняются автоматически, без участия программиста, то есть механизм виртуальной памяти является прозрачным по отношению к пользователю.
Наиболее распространенными способами реализации виртуальной памяти является страничное, сегментное и странично-сегментное распределение памяти, а также свопинг.
При страничном распределении памяти виртуальное адресное пространство каждого процесса делится на части одинакового, фиксированного для данной системы размера. Эти части называют виртуальными страницами. В общем случае размер виртуального адресного пространства не является кратным размеру страницы, поэтому последняя страница каждого процесса дополняется фиктивной областью. Вся оперативная память машины также делится на части такого же размера, называемые физическими страницами (или блоками). Размер страницы обычно выбирается равным степени числа 2, например: 512, 1024 и т.д. Это позволяет упростить механизм преобразования адресов.
При загрузке процесса часть его виртуальных страниц помещается в оперативную память, а остальные – на диск. Смежные виртуальные страницы не обязательно располагаются в смежных физических страницах. При загрузке операционная система создает для каждого процесса информационную структуру – таблицу страниц, в которой устанавливается соответствие между номерами виртуальных и физических страниц для страниц, загруженных в оперативную память, или делается отметка о том, что виртуальная страница выгружена на диск. Кроме того, в таблице страниц содержится управляющая информация, такая как признак модификации страницы, признак невыгружаемости (выгрузка некоторых страниц может быть запрещена), признак обращения к странице (используется для подсчета числа обращений за определенный период времени) и другие данные, формируемые и используемые механизмом виртуальной памяти.
При активизации очередного процесса в специальный регистр процессора загружается адрес таблицы страниц данного процесса. При каждом обращении к памяти происходит чтение из таблицы страниц информации о виртуальной странице, к которой произошло обращение. Если данная виртуальная страница находится в оперативной памяти, то выполняется преобразование виртуального адреса в физический. Если же нужная виртуальная страница в данный момент выгружена на диск, то происходит так называемое страничное прерывание. Выполняющийся процесс переводится в состояние ожидания, и активизируется другой процесс из очереди готовых. Параллельно программа обработки страничного прерывания находит на диске требуемую виртуальную страницу и пытается загрузить ее в оперативную память. Если в памяти имеется свободная физическая страница, то загрузка выполняется немедленно, если же свободных страниц нет, то решается вопрос, какую страницу следует выгрузить из оперативной памяти.
В данной ситуации может быть использовано много разных критериев выбора, наиболее популярные из них – «первая попавшаяся страница» или «страница, к которой в последнее время было меньше всего обращений».
В некоторых системах используется понятие рабочего множества страниц. Рабочее множество определяется для каждого процесса и представляет собой перечень наиболее часто используемых страниц, которые должны постоянно находиться в оперативной памяти и поэтому не подлежат выгрузке.
После того, как выбрана страница, которая должна покинуть оперативную память, анализируется ее признак модификации (по таблице страниц). Если выталкиваемая страница с момента загрузки была модифицирована, то ее новая версия должна быть переписана на диск. Если нет, то она может быть просто уничтожена, то есть соответствующая физическая страница объявляется свободной.
При каждом обращении к оперативной памяти с помощью аппаратных средств выполняются следующие действия:
1) на основании начального адреса таблицы страниц (содержимое регистра адреса таблицы страниц), номера виртуальной страницы (старшие разряды виртуального адреса) и длины записи в таблице страниц (системная константа) определяется адрес нужной записи в таблице;
2) из этой записи извлекается номер физической страницы;
3) к номеру физической страницы присоединяется смещение (младшие разряды виртуального адреса).
Использование в последнем пункте того факта, что размер страницы равен степени числа 2, позволяет применить операцию конкатенации (присоединения) вместо более длительной операции сложения, что уменьшает время получения физического адреса, а, следовательно, повышает производительность системы.
На производительность системы со страничной организацией памяти влияют затраты времени, связанные с обработкой страничных прерываний и преобразованием виртуального адреса в физический. При часто возникающих страничных прерываниях система может тратить большую часть времени впустую. Чтобы уменьшить частоту страничных прерываний, следовало бы увеличивать размер страницы. Кроме того, увеличение размера страницы уменьшает размер таблицы страниц, а значит, уменьшает затраты памяти. С другой стороны, если страница велика, следовательно, велика и фиктивная область в последней виртуальной странице каждого процесса. В среднем на каждом процессе теряется половина объема страницы, что в сумме при большой странице может составить существенную величину. Время преобразования виртуального адреса в физический в значительной степени определяется временем доступа к таблице страниц. В связи с этим таблицу страниц стремятся размещать в «быстрых» запоминающих устройствах. Это может быть, например, набор специальных регистров или память, использующая для уменьшения времени доступа ассоциативный поиск и кэширование данных.
Страничное распределение памяти может быть реализовано в упрощенном варианте без выгрузки страниц на диск. В этом случае все виртуальные страницы всех процессов постоянно находятся в оперативной памяти. Такой вариант страничной организации хотя и не предоставляет пользователю виртуальной памяти, но почти исключает фрагментацию за счет того, что, во-первых, программа может загружаться в несмежные области, а, во-вторых, при загрузке виртуальных страниц никогда не образуется остатков.
При сегментном распределение памяти виртуальное адресное пространство процесса делится на сегменты, размер которых определяется программистом с учетом смыслового значения содержащейся в них информации. Отдельный сегмент может представлять собой подпрограмму, массив данных и т.п. Иногда сегментация выполняется по умолчанию компилятором. При загрузке процесса часть сегментов помещается в оперативную память (при этом для каждого из этих сегментов операционная система подыскивает подходящий участок свободной памяти), а часть сегментов размещается в дисковой памяти. Сегменты одного процесса могут занимать в оперативной памяти несмежные участки. Во время загрузки система создает таблицу сегментов процесса (аналогичную таблице страниц), в которой для каждого сегмента указывается начальный физический адрес сегмента в оперативной памяти, размер сегмента, правила доступа, признак модификации, признак обращения к данному сегменту за последний интервал времени и некоторая другая информация. Если виртуальные адресные пространства нескольких процессов включают один и тот же сегмент, то в таблицах сегментов этих процессов делаются ссылки на один и тот же участок оперативной памяти, в который данный сегмент загружается в единственном экземпляре.
Система с сегментной организацией функционирует аналогично системе со страничной организацией: время от времени происходят прерывания, связанные с отсутствием нужных сегментов в памяти, при необходимости освобождения памяти некоторые сегменты выгружаются, при каждом обращении к оперативной памяти выполняется преобразование виртуального адреса в физический. Кроме того, при обращении к памяти проверяется, разрешен ли доступ требуемого типа к данному сегменту.
Недостатком данного метода распределения памяти является фрагментация на уровне сегментов и более медленное преобразование адреса по сравнению со страничной организацией памяти.
Странично-сегментное распределение памяти представляет собой комбинацию страничного и сегментного распределения памяти и, вследствие этого, сочетает в себе достоинства обоих подходов. Виртуальное пространство процесса делится на сегменты, а каждый сегмент в свою очередь делится на виртуальные страницы, которые нумеруются в пределах сегмента. Оперативная память делится на физические страницы. Загрузка процесса выполняется операционной системой постранично, при этом часть страниц размещается в оперативной памяти, а часть на диске. Для каждого сегмента создается своя таблица страниц, структура которой полностью совпадает со структурой таблицы страниц, используемой при страничном распределении. Для каждого процесса создается таблица сегментов, в которой указываются адреса таблиц страниц для всех сегментов данного процесса. Адрес таблицы сегментов загружается в специальный регистр процессора, когда активизируется соответствующий процесс.
Разновидностью виртуальной памяти является так называемый свопинг (swapping). В соответствии с методом свопинга некоторые процессы (обычно находящиеся в состоянии ожидания) временно выгружаются на диск. Планировщик операционной системы не исключает их из своего рассмотрения, и при наступлении условий активизации некоторого процесса, находящегося в области свопинга на диске, этот процесс перемещается в оперативную память. Если свободного места в оперативной памяти не хватает, то выгружается другой процесс.
При свопинге, в отличие от рассмотренных ранее методов реализации виртуальной памяти, процесс перемещается между памятью и диском целиком, то есть в течение некоторого времени процесс может полностью отсутствовать в оперативной памяти. Существуют различные алгоритмы выбора процессов на загрузку и выгрузку, а также различные способы выделения оперативной и дисковой памяти загружаемому процессу.
Рассмотрим иерархию запоминающих устройств (ЗУ) и принцип кэширования информации.
Память ВМ представляет собой иерархию ЗУ, включающую внутренние регистры процессора, различные типы сверхоперативной, оперативной и постоянной памяти, внешнюю память на магнитных дисках и других типах устройств. Разные типы ЗУ отличаются средним временем доступа и стоимостью хранения данных в расчете на один бит.
Кэширование информации–это способ организации совместного функционирования двух типов ЗУ, отличающихся временем доступа и стоимостью хранения данных, который позволяет уменьшить среднее время доступа к данным за счет динамического копирования наиболее часто используемой информации из относительно более «медленного» ЗУ в более «быстрое» ЗУ. Кэш-памятью обычно называют одно из таких взаимодействующих устройств, а именно «быстрое» ЗУ. Оно стоит дороже и, как правило, имеет сравнительно небольшой объем. Таким образом кэширование представляет собой некоторое компромиссное решение проблемы стоимости и быстродействия памяти. Важно, что механизм кэш-памяти является прозрачным для пользователя, то есть пользователь не должен сообщать никаких сведений об интенсивности использования информации и никак не должен участвовать в перемещении информации из ЗУ одного типа в ЗУ другого типа. Все это делается автоматически системными средствами.
Представим частный случай использования кэш-памяти для уменьшения среднего времени доступа к данным, хранящимся в оперативной памяти. Для этого между процессором и оперативной памятью помещается «быстрое» ЗУ – кэш-память. Содержимое кэш-памяти представляет собой совокупность записей обо всех загруженных в нее элементах данных. Каждая запись об элементе данных включает в себя адрес, который этот элемент данных имеет в оперативной памяти, и управляющую информацию: признак модификации и признак обращения к данным за некоторый последний период времени.
В системах, оснащенных кэш-памятью, каждый запрос к оперативной памяти выполняется в соответствии со следующим алгоритмом:
1) просматривается содержимое кэш-памяти с целью определения, не находятся ли нужные данные в кэш-памяти (кэш-память не является адресуемой, поэтому поиск нужных данных осуществляется по содержимому – значению поля «адрес в оперативной памяти», взятому из запроса);
2) если данные обнаруживаются в кэш-памяти, то они считываются из нее, и результат передается в процессор;
3) если нужных данных нет, то они вместе со своим адресом копируются из оперативной памяти в кэш-память, и результат выполнения запроса передается в процессор.
При копировании данных может оказаться, что в кэш-памяти нет свободного места, тогда для вытеснения из кэш-памяти выбираются данные, к которым в последний период было меньше всего обращений. Если вытесняемые данные были модифицированы за время нахождения в кэш-памяти, то они переписываются в оперативную память. Если же эти данные не были модифицированы, то их место в кэш-памяти объявляется свободным.
На практике в кэш-память считывается не один элемент данных, к которому произошло обращение, а целый блок данных. Это увеличивает вероятность так называемого «попадания в кэш», то есть нахождения нужных данных в кэш-памяти. В реальных системах вероятность попадания в кэш составляет примерно 0,9.
Высокое значение вероятности нахождения данных в кэш-памяти связано с наличием у данных объективных свойств: пространственной и временной локальности.
Под пространственной локальностью данных в кэш-памяти понимается такой случай, когда произошло обращение по некоторому адресу и с высокой степенью вероятности в ближайшее время произойдет обращение к соседним адресам.
Под временной локальностью данных в кэш-памяти понимается случай большой вероятности обращения по этому же адресу в ближайшее время.
Все предыдущие рассуждения справедливы и для других пар запоминающих устройств, например, для оперативной памяти и внешней памяти. В этом случае уменьшается среднее время доступа к данным, расположенным, например, на магнитном диске, а роль кэш-памяти выполняет буфер в оперативной памяти.
Управление вводом-выводом
Одной из главных функций ОС является управление всеми устройствами ввода-выводаВМ. ОС должна передавать устройствам команды, перехватывать прерывания и обрабатывать ошибки; она также должна обеспечивать интерфейс между устройствами и остальной частью машины. В целях развития интерфейс должен быть одинаковым для всех типов устройств (принцип независимости от устройств).
Устройства ввода-вывода делятся на два типа: блок-ориентированные устройства и байт-ориентированные устройства.
Блок-ориентированные устройства ввода-вывода хранят информацию в блоках фиксированного размера, каждый из которых имеет свой собственный адрес. Самое распространенное блок-ориентированное устройство – диск.
Байт-ориентированные устройства ввода-выводане адресуемы и не позволяют производить операцию поиска, они генерируют или потребляют последовательность байтов. Примерами являются мониторы, принтеры, сетевые адаптеры. Однако некоторые внешние устройства не относятся ни к одному классу, например, часы, которые, с одной стороны, не адресуемы, а с другой стороны, не порождают потока байтов. Это устройство только выдает сигнал прерывания в некоторые моменты времени.
Любое внешнее устройство обычно состоит из механического и электронного компонента. Электронный компонент называют контроллером устройства или адаптером. Механический компонент представляет собственно устройство. Некоторые контроллеры могут управлять несколькими устройствами. Если интерфейс между контроллером и устройством стандартизован, то независимые производители могут выпускать совместимые как контроллеры, так и устройства.
ОС обычно имеет дело не с устройством, а с его контроллером. Контроллер, как правило, выполняет простые функции, например, преобразует поток бит в блоки (состоящие из байт), осуществляют контроль и исправление ошибок. Каждый контроллер имеет несколько регистров, которые используются для взаимодействия с центральным процессором. В некоторых ВМ эти регистры являются частью физического адресного пространства. В таких ВМ нет специальных операций ввода-вывода. В других машинах адреса регистров ввода-вывода, называемых часто портами, образуют собственное адресное пространство за счет введения специальных операций ввода-вывода.
ОС выполняет ввод-вывод, записывая команды в регистры контроллера. Когда команда принята, процессор оставляет контроллер и занимается другой работой. При завершении команды контроллер организует прерывание для того, чтобы передать управление процессором операционной системе, которая должна проверить результаты операции. Процессор получает результаты и статус устройства, читая информацию из регистров контроллера.
Основная идея организации программного обеспечения ввода-вывода состоит в разбиении его на несколько уровней, причем нижние уровни обеспечивают экранирование особенностей аппаратуры от верхних, а те, в свою очередь, обеспечивают удобный интерфейс для пользователей. Ключевым принципом является независимость от устройств ввода-вывода. Вид программы не должен зависеть от того, читает ли она данные с гибкого диска или с жесткого диска.
Очень близкой к идее независимости от устройств является идея единообразного именования, то есть для именования устройств должны быть приняты единые правила.
Другим важным вопросом для программного обеспечения ввода-вывода является обработка ошибок. В лучшем случае ошибки следует обрабатывать как можно ближе к аппаратуре. Если контроллер обнаруживает ошибку чтения, то он должен попытаться ее скорректировать. Если же это ему не удается, то исправлением ошибок должен заняться драйвер устройства. Многие ошибки могут исчезать при повторных попытках выполнения операций ввода-вывода, например, ошибки, вызванные наличием пылинок на головках чтения или на диске. И только если нижний уровень не может справиться с ошибкой, он сообщает об ошибке верхнему уровню.
Еще один ключевой вопрос – это использование блокирующих (синхронных) и неблокирующих (асинхронных) передач. Большинство операций физического ввода-вывода выполняется асинхронно – процессор начинает передачу и переходит на другую работу, пока не наступает прерывание. Пользовательские программы намного легче писать, если операции ввода-вывода блокирующие. ОС выполняет операции ввода-вывода асинхронно, но представляет их для пользовательских программ в синхронной форме.
Проблема состоит также в том, что одни устройства ввода-вывода являются разделяемыми, а другие – выделенными. Диски – это разделяемые устройства, так как одновременный доступ нескольких пользователей к диску не является проблемой. Принтеры – это выделенные устройства, потому что нельзя смешивать строчки, печатаемые различными пользователями. Наличие выделенных устройств создает для операционной системы некоторые проблемы.
Для решения имеющихся проблем целесообразно разделить программное обеспечение ввода-вывода на четыре слоя:
1) обработка прерываний;
2) драйверы устройств;
3) слой операционной системы, независимый от устройств;
4) пользовательский слой программного обеспечения.
Прерывания должны быть скрыты как можно глубже в «недрах» ОС, чтобы как можно меньшая часть ОС имела с ними дело. Наилучший способ состоит в разрешении процессу, инициировавшему операцию ввода-вывода, блокировать себя до завершения операции и наступления прерывания. При наступлении прерывания процедура обработки прерывания выполняет разблокирование процесса, инициировавшего операцию ввода-вывода. В любом случае эффект от прерывания будет состоять в том, что ранее заблокированный процесс теперь продолжит свое выполнение.
Весь зависимый от устройства код помещается в драйвер устройства. Каждый драйвер управляет устройствами одного типа или, может быть, одного класса. В ОС, как правило, только драйвер устройства обладает информацией о конкретных особенностях какого-либо устройства.Например, только драйвер диска имеет дело с дорожками, секторами, цилиндрами, временем установления головки и другими факторами, обеспечивающими правильную работу диска.
Драйвер устройства принимает запрос от устройств программного слоя и решает, как его выполнить. Типичным запросом является, например, чтение N блоков данных. Если драйвер был свободен во время поступления запроса, то он начинает выполнять запрос немедленно. Если же он был занят обслуживанием другого запроса, то вновь поступивший запрос присоединяется к очереди уже имеющихся запросов, и он будет выполнен, когда наступит его очередь.
Первый шаг в реализации запроса ввода-вывода, например, для диска, состоит в преобразовании его из абстрактной формы в конкретную. Для дискового драйвера это означает, во-первых, преобразование номеров блоков в номера цилиндров, головок, секторов, во-вторых, проверку, работает ли двигатель, находится ли головка над нужным цилиндром и т.п. Короче говоря, он должен решить, какие операции контроллера нужно выполнить и в какой последовательности. После передачи команды контроллеру драйвер должен решить следующий вопрос: блокировать ли себя до окончания заданной операции или нет. Если операция занимает значительное время (например, при печати некоторого блока данных), то драйвер блокируется до тех пор, пока операция не завершится, и обработчик прерывания не разблокирует его. Если команда ввода-вывода выполняется быстро (например, прокрутка экрана), то драйвер ожидает ее завершения без блокирования.
Большая часть программного обеспечения ввода-вывода является независимой от устройств. Точная граница между драйверами и программами, независимыми от устройств, определяется системой, так как некоторые функции, которые могли бы быть реализованы независимым способом, в действительности выполнены в виде драйверов для повышения эффективности или по другим причинам.
Типичными функциями слоя операционной системы, независимого от устройств, являются:
– обеспечение общего интерфейса к драйверам устройств;
– именование устройств;
– защита устройств;
– обеспечение независимого размера блока;
– буферизация;
– распределение памяти на блок-ориентированных устройствах;
– распределение и освобождение выделенных устройств;
– уведомление об ошибках.
Остановимся на некоторых функциях данного перечня. Верхним слоям программного обеспечения неудобно работать с блоками разной величины, поэтому данный слой обеспечивает единый размер блока, например, за счет объединения нескольких различных блоков в единый логический блок. В связи с этим верхние уровни имеют дело с абстрактными устройствами, которые используют единый размер логического блока независимо от размера физического сектора.
При создании файла или заполнении его новыми данными необходимо выделить ему новые блоки. Для этого ОС должна вести список или битовую карту свободных блоков диска. На основании информации о наличии свободного места на диске может быть разработан алгоритм поиска свободного блока, независимый от устройства и реализуемый программным слоем, находящимся выше слоя драйверов.
Хотя большая часть программного обеспечения ввода-вывода находится внутри ОС, некоторая его часть содержится в библиотеках, связываемых с пользовательскими программами. Системные вызовы, включающие вызовы ввода-вывода, обычно делаются библиотечными процедурами. В частности, форматирование ввода или вывода выполняется библиотечными процедурами. Стандартная библиотека ввода-вывода содержит значительное число процедур, которые выполняют ввод-вывод и работают как часть пользовательской программы.
Другой категорией программного обеспечения ввода-вывода является подсистема спулинга (spooling). Спулинг – это способ работы с выделенными устройствами в многозадачной системе. Одно из типичных устройств, требующих спулинга, – принтер. Хотя технически легко позволить каждому пользовательскому процессу открыть специальный файл, связанный с принтером, такой способ опасен из-за того, что пользовательский процесс может монополизировать принтер на произвольное время. Вместо этого создается специальный процесс-монитор, который получает исключительные права на использование этого устройства. Также создается специальный каталог, называемый каталогом спулинга. Для того, чтобы напечатать файл, пользовательский процесс помещает выводимую информацию в этот файл и помещает его в каталог спулинга. Процесс-монитор по очереди распечатыв