2. Основы работы многозадачных ОС
Архитектура ОС
Все ОС строятся по модульному принципу (планирование процессов, управление памятью, подсистема ввода-вывода и т.д.), и для них очень важны способы взаимодействия между модулями (межмодульный интерфейс), для каждого из которых определены спецификации функционирования и интерфейсы. ОС, однако, различаются по способам оформления модулей и связей между ними. Модульный состав и организация межмодульного взаимодействия и составляют архитектуру ОС.
Первой из четко сформулированных архитектурных концепций ОС была иерархия абстрактных машин. Самый нижний уровень иерархии составляет реальная машина с ее интерфейсом оборудования. Нижний слой программного обеспечения составляет первый уровень. Совместно с аппаратными средствами он представляет некоторую абстрактную машину со своим, более высокоуровневым интерфейсом оборудования. На основе этого интерфейса строится абстрактная машина второго уровня и т.д. Последовательным наращиванием слоев программного обеспечения интерфейс абстрактной машины доводится до уровня интерфейса процессов.
Следует отдельно рассмотреть архитектурную концепцию, получившую название ОС виртуальных машин. В ОС виртуальных машин интерфейс процесса выглядит как интерфейс оборудования. В предельном случае внешние формы этих двух интерфейсов совпадают полностью. В этом случае процессу доступны все команды из набора команд вычислительной системы. Но на самом деле выдача процессом привилегированной команды вызывает прерывание-ловушку. Обрабатывая ловушку, ОС выполняет для процесса требуемую команду или моделирует выполнение этой команды на виртуальном оборудовании. У процесса создается иллюзия, что в его полном распоряжении находится реальная вычислительная система
Концепция микроядра подразумевает наличие «мини-ОС», реализующей наиболее фундаментальные, критичные по времени и аппаратно-зависимые функции управления, чаще всего это: управление реальной памятью, переключение процессов, средства обмена сообщениями между процессами, а в мультипроцессорных системах – и управление загрузкой процессоров. Концепция микроядра предполагает оформление модулей ОС в виде процессов. ОС, построенные на базе микроядра обладают гибкостью в настройке, т.к. модули ОС, не входящие в микроядро могут легко заменяться, и потенциальной переносимостью, т.к. для перенесения ОС на другую платформу достаточно только переписать микроядро.
В объектно-ориентированных ОС ресурсы представляются в виде объектов – полностью инкапсулированных структур данных. Для каждого типа объектов определен набор допустимых операций над ним. Наряду с общими преимуществами, обеспечиваемыми ОО-подходом (полиморфизм, инкапсуляция), такие ОС позволяют обеспечить надежную защиту системных и пользовательских ресурсов.
Реальные ОС, как правило, сочетают в себе комбинацию различных архитектурных подходов.
Ядро ОС.
Все современные ОС предполагают выполнение программных задач на аппаратной платформе, обеспечивающей по крайней мере 2 режима функционирования процессора:
- Режим ядра (супервизора) – в котором программа может выполнять все команды процессора
- Режим процесса (задачи) – программа может выполнять лишь некоторое подмножество команд, в которое не входят команды управления системой.
Под процессом подразумевается загруженная в память и готовая к исполнению программа.
Та часть ОС, которая работает в режиме ядра, так и называется ядром. Процессы пользователей работают в режиме процесса. Большая (количественно) часть модулей ОС также работает в режиме процесса.
На ядро, как правило, возлагаются такие основные функции:
- обработка прерываний;
- создание и уничтожение процессов;
- переключение процессов из одного состояния в другое;
- синхронизация и взаимодействие процессов;
- поддержка операций ввода-вывода;
- поддержка файловой системы;
- учет работы системы и использования ресурсов пользователями;
Для ОС процесс представляется блоком контекста процесса. Блок контекста содержит информацию о процессе, необходимую для ОС, в том числе информацию обо всех выделенных процессу ресурсах. Обязательным для блока контекста является вектор состояния процессора, составляющие которого соответствуют содержимому аппаратных регистров процессора. В большинстве процессорных архитектур вектор состояния может быть загружен в соответствующие регистры или считан из них в память одной или несколькими командами.
Действия ОС по управлению процессами сводятся к манипуляциям с блоками контекста процессов и с отдельными полями этих блоков. Процесс в системе может находиться в различных состояниях. Количество состояний процесса разное в разных ОС, но все они сводятся к трем основным, показанным на рис. 2.
Рис 2. Виды состояний процесса
- Активное состояние – процесс имеет все необходимые для выполнения ресурсы, в том числе и ресурс центрального процессора. Активный процесс выполняется.
- Готовое состояние – процесс имеет все необходимые для выполнения ресурсы, кроме ресурса процессора.
- Заблокированное (ожидающее) состояние – процессу не хватает еще какого-либо ресурса (ресурсов).
Процесс, поступивший в систему, создается в заблокированном состоянии, пока ему не будут выделены необходимые ресурсы. Получив ресурсы, процесс попадает в очередь готовых процессов, где и ожидает, когда ему будет выделен процессор. Процесс, получивший процессор, становится активным и выполняется. Активный процесс переходит в блокированное состояние, когда выдает системный вызов – запрос на ресурсы, которые не могут быть ему предоставлены немедленно (например, выполнение операции ввода-вывода). При этом процесс попадает в очередь к тому ресурсу, который он запрашивает. Процесс может перейти из активного состояния в готовое либо по собственной инициативе, добровольно отказавшись от использования центрального процессора, либо по инициативе ОС.
Выборку процессов из очереди на входе системы называют планированием задач, выборку из очередей блокированных в готовое состояние– планированием ресурсов, выборку из очереди готовых в активное – планированием процессов. Выборка из каждой очереди осуществляется по собственной дисциплине отдельным модулем-планировщиком в составе ОС.
Процессы и потоки
Каждый процесс имеет свое собственное виртуальное адресное пространство (≤ 4Gb). Процесс состоит из кода, данных и других системных ресурсов, таких как открытые файлы, каналы (pipes), синхронизирующие объекты.
Поток (thread) – базовый объект, которому ОС распределяет время центрального процессора.
Исполнение процесса начинается со стартового потока. В дальнейшем он может порождать другие потоки. Ресурсы процесса доступны всем его потокам. Каждый поток использует структуру данных, для сохраненияконтекста исполнения , в то время, когда у него отнимается процессор. В контекст входят регистры процессора, переменные окружения, стеки ядра и пользователя. Все потоки одного процесса совместно используют его виртуальное адресное пространство.
Процессорное время распределяется по очереди между потоками, а не между процессами. Продолжительность кванта времени, выделяемого потоку, может изменяться в широких пределах. Обычно это около 20 мс.
Если ОС выполняет большой объем работ по управлению ресурсами, то создание нового процесса и выделение ему ресурсов не может обойтись без значительных "накладных расходов". Но для обеспечения распараллеливания работ по решению нескольких задач или внутри одной задачи выгодно иметь возможность «дешевого» создания процессов. Преодоление этого противоречия было найдено в концепции "потоков" (thread), реализованной в большинстве современных ОС.
Потоком называется часть процесса, которая использует большинство выделенных процессу ресурсов, но является самостоятельным объектом при планировании процессорного времени. Таким образом, потоки одного процесса могут выполняться параллельно. В большинстве современных ОС помимо ресурса процессорного времени и вектора состояния процесса, поток имеет собственный приоритет и собственный стек (в стеке же размещаются и локальные переменные). Прочие же ресурсы: общие переменные, файлы, средства взаимодействия и т.д. – являются общими для потока и породившего его процесса.
Любой процесс состоит из одного или нескольких потоков. Первый поток – основной – порождается системой при запуске процесса. Основной поток может порождать дочерние потоки специальным системным вызовом порождения потоков. В программе поток выглядит как обычная функция/процедура. Вызов порождения передает управление на эту функцию и далее выполнение функции происходит параллельно с выполнением основной программы. Поскольку при порождении каждого нового потока выделяется отдельный стек, одна и та же функция может выполняться в составе двух и более параллельных потоков. При порождении новый поток наследует приоритет родителя, но далее этот приоритет может быть изменен.
Распределение времени между потоками. Процессорное время выделяется потокам в соответствии с их уровнем приоритета. Потоку с более низким приоритетом не выделяется время, если на него претендует поток с более высоким уровнем приоритета. Более того, процесс с более низким приоритетом прерывается до истечения кванта времени, если на процессор претендует поток с более высоким уровнем приоритета. Необходимо помнить, что в среде ОС основная “работа” потока состоит в ожидании события и реагировании на него. Это дает шанс на исполнение потокам с низким уровнем приоритета.
Уровни приоритетов варьируются в диапазоне от 0 (низший) до 31 (высший) (для ОС Windows).
Уровень приоритета каждого потока состоит из трех составляющих
- класс приоритета процесса (простаивающий, нормальный, высокий, реального времени);
- уровень приоритета потока внутри класса приоритета процесса (нижний, ниже нормального, нормальный, выше нормального, высший)
- динамически установленный уровень приоритета.
При запуске на исполнение процесса ему назначается один из четырех классов приоритета. Уровень Idle (простаивающий) назначается процессу, который ничего не должен делать в случае активности других процессов (например, хранитель экрана). Процессам, запускаемым пользователем, присваивается нормальный уровень. Пользователь может запустить несколько процессов. Тому процессу, с которым пользователь непосредственно работает (а это может быть только один процесс), уровень приоритета поднимается на две единицы (например: 7+2 = 9). Это делает общение с прикладной программой более “комфортабельным”. Высокий класс приоритета назначается некоторым системным процессам, которые простаивают до возникновения определенных событий и, поэтому, не мешают остальным процессам. Только в особых случаях процесс может относиться к классу Real time.
Планирование процессов
К дисциплине планирования, в общем случае может применяться широкий спектр требований, наиболее существенные из которых следующие:
- она должна быть справедливой – не давать преимуществ одним процессам за счет других и ни в коем случае не допускать бесконечного откладывания процессов,
- она должна обеспечивать максимальную пропускную способность системы – выполнение максимального количества единиц работы (процессов) в единицу времени;
- она должна обеспечивать приемлемое время реакции для интерактивных пользователей;
- она должна обеспечивать гарантированное время реакции для процессов реального времени;
- она должна быть предсказуемой – дисперсия времен выполнения процессов, обладающих одинаковыми характеристиками, должна быть минимальной;
- она должна учитывать внешние приоритеты, присваиваемые процессам пользователем и/или администратором системы;
- накладные расходы по ее реализации (затраты процессорного времени и др. ресурсов) должны быть минимизированы;
- она должна учитывать комплексное использование ресурсов вычислительной системы, обеспечивая высокую загрузку системы в целом и рациональное использование ключевых ресурсов.
Очевидно, что выполнение всех перечисленных требований в одинаковой степени невозможно, так как некоторые из них противоречат друг другу. В конкретных системах те или иные требования выдвигаются на передний план – в зависимости от задач системы и характеристик выполняемых в ней процессов.
С точки зрения реализации дисциплины планирования подразделяются, прежде всего, на дисциплины вытесняющие и невытесняющие (кооперативные). Для первых возможно прерывание активного процесса и лишение его ресурса ЦП по инициативе планировщика, для вторых – нет. Дисциплины с вытеснением выполняют более частые переключения процессов, следовательно, имеют большие накладные расходы. Но в большинстве случаев только они могут обеспечить требуемые показатели справедливости обслуживания.
Классификация дисциплин также определяется способом определения приоритетов процессов. Различают приоритеты:
- внешние – назначаемые администратором системы или пользователем в соответствии с классом пользователя и/или произведенной пользователем оплатой;
- статические – вычисляемые планировщиком при поступлении процесса в систему и не изменяемые впоследствии;
- динамические – перевычисляемые планировщиком периодически или/и при событиях, влияющих на планирование процессов;
- комплексные – динамически вычисляемые приоритеты, учитывающие внешний приоритет процесса, его статические характеристики, а также ход выполнения процесса, текущее состояние очереди готовых процессов и, возможно, состояние других системных ресурсов.
Среди базовых дисциплин планирования процессов можно назвать:
- “Первым пришел – первым обслуживается” – простейшая дисциплина без вытеснения, работа которой понятна из ее названия. Обеспечивает минимальные накладные расходы и гарантирует отсутствие бесконечного откладывания, но не обеспечивает справедливости для коротких процессов.
- “Карусель” – простейшая дисциплина с вытеснением. Процесс получает в свое распоряжение ЦП на некоторый квант времени Q (в простейшем случае– Q=const.). Если за время Q процесс не завершился, он вытесняется с ЦП и направляется в конец очереди готовых процессов, где ждет выделения ему следующего кванта, и т.д. Карусель обеспечивает наилучшие показатели справедливости. Эффективности карусели существенно зависит от выбора величины кванта Q.
- “Самая короткая работа – следующая” – наивысший приоритет имеет самый короткий процесс. Для того, чтобы применять эту дисциплину, должна быть заранее известна длительность процесса. Обеспечивает максимальную пропускную способность системы. Для коротких процессов дисциплина обеспечивает наилучшие показатели, но показатели для длинных процессов значительно хуже. Дисциплина может применяться как в вытесняющем, так и в невытесняющем варианте.
- “С наибольшим штрафным отношением – следующий” – дисциплина без вытеснения, обеспечивающая даже лучшие показатели справедливости, чем карусель за счет динамического переопределения приоритетов. Всякий раз при освобождении ЦП для всех готовых процессов вычисляется текущее отношение потерянного/использованного времени и наивысший приоритет имеет процесс с наибольшим значением этого отношения.
- “Многоуровневые очереди с обратной связью” предполагают расщепление очереди готовых процессов на две и более подочереди – более длинные (использовавшие больше времени ЦП) процессы попадают в очереди с большими номерами и, соответственно, с меньшими приоритетами. Селекция процессов по длительности, таким образом, происходит динамически. Разные очереди могут обслуживаться по разным дисциплинам.
В реальных ОС при планировании процессорного времени применяются модификации и/или комбинации базовых алгоритмов, обеспечивающие большую эффективность и гибкость. Процессы в реальных ОС, как правило, делятся на классы. Типовое деление включает в себя три класса:
- с высоким приоритетом – процессы реального времени;
- с нормальным приоритетом – интерактивные процессы;
- с низким приоритетом – счетные (пакетные) процессы.
Обычно внутри класса с нормальным приоритетом существует еще несколько градаций, которые для процесса могут вычисляться динамически. Типичные правила динамического перевычисления приоритетов сводятся к таким:
- приоритет процесса, долгое время находящегося в состоянии ожидания, повышается;
- приоритет процесса, часто выполняющего операции ввода-вывода, повышается;
- приоритет процесса, чаще получающего внешние сообщения и прерывания, повышается;
- если приоритет процесса не повышается, он убывает.
Управление памятью
Виртуальная и реальная память
В редких случаях программист задает при разработке программы реальные адреса в оперативной памяти, в большинстве же случаев между программистом и средой выполнения его программы стоит тот или иной аппарат преобразования адресов. В общем случае то адресное пространство, в котором пишется программа, называется виртуальною памятью в отличие от реальной или физической памяти – в которой происходит выполнение процесса.
Преобразование виртуальных адресов в реальные (привязка адресов) может происходить при загрузке программы или при выполнении программы. В настоящее время только в ОС MS DOS привязка адресов выполняется при загрузке, во всех же современных системах– при выполнении. При таком подходе привязка выполняется при каждом обращении к памяти, то есть очень часто. Поэтому естественным решением является выполнение этой функции на аппаратном уровне. Аппаратура переводит виртуальные адреса в реальные, используя некоторые таблицы привязки. Подготовка таблиц привязки адресов – операция, выполняемая для процесса одноразово, модификация их производится также одноразово, поэтому задача формирования и модификации таблиц привязки возлагается на ОС. Разработка программ, работающих в виртуальном адресном пространстве, имеет целый ряд преимуществ, которые можно сгруппировать по трем основным направлениям.
1. Удобство для программиста. Программист имеет в своем распоряжении виртуальную память, представляющую собой адресное пространство либо совершенно плоское – с адресами, линейно возрастающими от 0 до максимального значения, либо сегментированное в соответствии с потребностями задачи. При этом он не заботится о том, как будет размещен его процесс в реальной памяти.
2. Реорганизация памяти. Системные средства управления памятью могут выбрать такое отображение виртуальной памяти в реальную, которое обеспечит максимально эффективное использование реальной памяти. В случае, если обеспечивается аппаратная привязка адресов, реорганизация реальной памяти может производиться и в ходе выполнения процесса прозрачно для последнего.
3. Защита. Процесс никак не может обратиться за пределы своего виртуального пространства, никакой процесс не будет иметь доступа к адресному пространству другого процесса.
4. При наличии аппаратной поддержки системы виртуальной памяти позволяют работать с виртуальными адресными пространствами, размер которых превышает доступный размер оперативной памяти. Это достигается за счет хранения части программ и данных на внешней(дисковой) памяти и управления миграцией данных между оперативной и внешней памятью (свопинг).
Модели виртуальной памяти.
Традиционные моделей виртуальной памяти, использующие развитые средства аппаратной трансляции адресов сводятся к: сегментной; страничной; комбинированной сегментно-страничной.
В сегментной модели распределение реальной памяти производится блоками переменной длины (сегментами), в страничной – блоками постоянной длины (страницами). Первая модель удобнее для программиста, так как позволяет структурировать адресное пространство программы, вторая– для ОС, т.к. управлять страницами одинакового размера проще. Современные аппаратные средства (Intel-Pentium) поддерживают сегментно-страничную модель, в которой адресное пространство процесса состоит из набора сегментов, а каждый сегмент– из набора страниц. Свопинг ведется на уровне страниц. Для обеспечения возможности миграции страниц из оперативной памяти на внешнюю и обратно аппаратура поддерживает дескриптор для каждой виртуальной страницы, в котором имеются признаки: присутствия виртуальной страницы в реальной памяти, обращения к странице, обращения к странице для записи. В случае нехватки оперативной памяти для вытеснения выбирается страница, к которой дольше всего не было обращений.
В некоторых современных системах на базе 32-разрядных процессоров (Windows 9х, Windows NT, OS/2) обеспечивается плоская модель, в которой виртуальное адресное пространство не сегментируется. Однако, методы такового обеспечения различны. Windows 9х просто отводит каждому процессу единственный сегмент размером 4Гбайт и, таким образом, сводит плоскую модель к страничной. Сама ОС располагается в верхней части этого адресного пространства и, таким образом, не гарантируется ее надежная защита. OS/2 выделяет процессу несколько сегментов, но обеспечивает прозрачный для процесса переход между сегментами. ОС также располагается в верхней части адресного пространства, но в отдельных сегментах, таким образом, ее защита надежнее. Windows NT также создает один сегмент, но верхняя часть адресного пространства содержит не саму ОС, а только переходники– обращения к защищенным процессам ОС, порча этой части адресного пространства не приведет к порче ОС.
Дальнейшее расширение разрядной сетки процессоров может привести к появлению совершенно новых моделей памяти. Уже сейчас одна из таких моделей– одноуровневая – реализована в вычислительной системе AS/400. 64-разрядное адресное слово позволяет процессу иметь плоскую виртуальную память размером до 64 Гэксабайт (!). В AS/400 эта возможность позволяет реализовать два принципиально важных свойства модели памяти:
- в виртуальное адресное пространство процесса включается не только оперативная память, но вся память – и оперативная и внешняя – имеющаяся в системе;
- все процессы работают в одном и том же виртуальном адресном пространстве, разделяя его, защита памяти обеспечивается дополнительными программно-аппаратными средствами;
Порождение программ и процессов
Компиляция, компоновка и загрузка
Этот этап реализуется не ОС, а системами программирования, основным функциональным назначением которых является генерация объектного кода программы (машинных команд). Основным компонентом системы программирования является компилятор. На вход компилятора поступает исходный модуль– текст программы на языке программирования. Выходом компилятора является объектный модуль – программа в машинных кодах, к которой, однако, еще не подключены внешние процедуры и функции. Компилятор предварительно формирует структуру виртуального адресного пространства: определяют состав сегментов и формируют содержимое (образы) кодовых сегментов и сегментов инициализированных статических данных. С целью получения наиболее эффективного объектного кода компиляторы могут выполнять оптимизацию обрабатываемой программы. Для программ, предназначенных к выполнению на RISC-процессорах, оптимизация является обязательной, так как позволяет эффективно загрузить конвейерные линии процессора.
Формирование единой программы из нескольких объектных модулей называется компоновкой. Обеспечение обращений к внешним по отношению к модулю именам называется связыванием. Операции компоновки и связывания выполняются специальными программами – компоновщиками (редакторами связей) или связывающими загрузчиками. Операцию связывания можно выполнять на разных этапах:
- статическое связывание этапа подготовки – выполняется компоновщиком, вызываемые модули включаются в загрузочный модуль программы;
- статическое связывание этапа загрузки – выполняется связывающим загрузчиком, вызываемые модули подключаются к программе уже в оперативной памяти;
- динамическое связывание этапа загрузки – отличается от предыдущего варианта тем, что обеспечивает совместное использование одних и тех же копий модулей в памяти разными программами; в некоторых реализациях модули загружаются в память совместно с основной программой, связи устанавливаются при загрузке или при обращении;
- динамическое связывание этапа выполнения – загрузка модулей и установка связей происходит при выполнении программы и управляет ими сама программа.
Современные ОС (Windows 9х, OS/2 и т.д.) позволяют сочетать статическую компоновку и связывание с динамической. Модули, подключаемые к программам динамически, носят название библиотек динамической компоновки (dynamic link library), соответственно, файлы, содержащие образы таких модулей имеют расширения DLL. DLL широко используются для системного программного обеспечения. API ОС, средства работы с терминалом, графические средства и т.п. представляют собой библиотеки динамической компоновки, совместно используемые всеми программами. При загрузке любой программы эти модули, как правило, уже находятся в памяти.
Цикл жизни процесса
Программа, готовая к выполнению, превратится в процесс только тогда, когда ОС создаст для нее блок контекста и запись в системной таблице процессов. ОС существенно различаются по тому признаку, насколько часто они создают новые процессы и сколько процессов могут одновременно существовать в системе. В однозадачных системах существует один процесс (или несколько процессов, только один из которых – пользовательский), который последовательно выполняет одну программу за другой. Различные подходы могут применяться в многозадачных интерактивных системах. Во-первых, с каждым терминалом может связываться единственный процесс-сеанс, пользователь каждого терминала работает как бы в однозадачной среде. Во-вторых, для преодоления стесненности пользователя в сеансе система может позволять ему в ходе сеанса порождать дополнительные процессы. В-третьих, система может позволять порождать любые процессы и в любом количестве.
Процессы в системе могут либо существовать как отдельные не связанные друг с другом единицы, либо образовывать какие-либо структуры. Обычно модель независимого существования процессов используется в тех ОС, которые накладывают ограничения на возможности создания процессов.
В тех ОС, которые разрешают процессам в свою очередь порождать новые процессы, применяется иерархическая структура связей между процессами, в которой между процессами существуют отношения "родитель-потомок". Передача ресурсов от предка к потомку позволяет родственным процессам легко устанавливать связи друг с другом. Процесс-потомок может быть запущен синхронно или асинхронно с родителем. При синхронном запуске процесс-родитель блокируется до завершения процесса-потомка. При асинхронном запуске родитель и потомок продолжают выполняться параллельно и могут взаимодействовать. Если родителю необходимо дождаться завершения потомка, он выдает системный вызов ожидания. Синхронный запуск не является обязательной возможностью, так как тот же эффект может быть обеспечен парой вызовов: "асинхронный запуск" – "ожидание".
Для того, чтобы родитель мог воздействовать на потомка, вызов запуска возвращает ему идентификатор (манипулятор) порожденного процесса. Этот идентификатор используется родителем при последующих воздействиях на потомка. Потомок может в свою очередь создавать новые процессы, то есть, стать во главе целого подсемейства процессов.
Монопольно используемые ресурсы
Свойства ресурсов и их представление
Процессорное время и оперативная память являются ключевыми ресурсами любой ОС, без них не может выполняться ни один процесс. Прочие ресурсы обладают рядом характеристических свойств.
Ресурсы могут быть монопольно используемыми или разделяемыми. Монопольными являются внешние устройства и некоторые системные структуры данных.
Ресурсы могут быть повторно используемыми или потребляемыми. Примеры первых – тот же ЦП или память; примеры вторых – входные данные и сообщения.
Ресурсы обладают также свойствами дискретности и ограниченности. Первое означает, что ресурсы распределяются некоторыми неделимыми единицами. Второе – то, что число единиц ресурса всегда небесконечно (процессорное время– непрерывно и бесконечно, память всегда конечна, а непрерывность или дискретность ее зависит от принятой модели памяти)
Процесс ни в коем случае не может самостоятельно завладеть ресурсом – а только через посредство ОС. Для предоставления процессам такой возможности в составе API ОС должны быть системные вызовы получения/освобождения ресурса. При запросе на получение ресурса ОС возвращает процессу манипулятор выделенного ресурса или– если выделение ресурса невозможно – блокирует процесс до освобождения требуемого ресурса.
Для каждого класса ресурсов ОС должна поддерживать дескриптор класса, в который должны входить: идентификатор класса; общее число единиц в классе; число свободных единиц; таблица единиц ресурса; список процессов, ожидающих ресурс этого класса; точка входа в менеджер класса; и т.д. Для каждой единицы ресурса имеется запись в таблице единиц, содержащая, как минимум, индикатор занятости ресурса и идентификатор процесса, которому ресурс распределен (если он не свободен).
Информация о ресурсах, выделенных процессу, также хранится в блоке контекста процесса.
Монопольные ресурсы и тупики
При наличии в системе монопольно используемых ресурсов возможно возникновение тупиков – ситуаций, когда два или более процесса требуют одних и тех же ресурсов и не друг другу дают возможности выполняться. Такие системы должны решать задачи:
- предупреждения тупиков – какую стратегию распределения ресурсов выбрать, чтобы тупики не возникали вообще?
- обнаружения тупиков – если не удалось применить стратегию, предупреждающую тупики, то как обнаружить возникший тупик?
- развязки тупиков – если тупик обнаружен, то как от него избавиться?
Решение этих задач достаточно сложно и всегда сопряжено с потерями: если система применяет либеральную политику выделения ресурсов (т.е. выделяет ресурс практически по любому требованию), то тупики становятся возможными и тогда их надо обнаруживать и развязывать; если же применяется консервативная политика (отказ, если выделение ресурса может грозить возникновением тупика), то снижается эффективность использования ресурсов.
Радикальное решение, к которому стремятся современные ОС, – замена реальных монопольных ресурсов виртуальными разделяемыми. Так, например, два процесса одновременно выполняют вывод данных на принтер. Принтер является монопольным ресурсом. Но на самом деле выводимые данные процесса попадают не непосредственно на принтер, а в так называемую область спулинга на диске (разделяемый ресурс)– для каждого процесса своя область. Затем ОС сама организует последовательный вывод данных, накопленных в областях спулинга на реальный принтер.
Управление вводом-выводом
Виртуальные устройства
ОС конструирует ресурсы высокого уровня – виртуальные устройства и предоставляет пользователю интерфейс для работы с ними. Можно в общем случае определить четыре метода, которые могут использоваться ОС для конструирования виртуальных устройств
- Метод закрепления однозначно отображает виртуальное устройство на реальное устройство, применение этого метода порождает все проблемы, связанные с использованием монопольных ресурсов.Метод разделения применим к устройством, ресурс которых является делимым. В этом случае ресурс устройства разбивается на части, каждая из которых закрепляется за одним процессом.
- Метод спулинга заставляет процесс обмениваться данными не с реальным устройством, а с некоторой буферной областью в памяти (оперативной или внешней). Обмен же данными между буфером и реальным устройством организует сама ОС. Буферизация прозрачна для процессов и может создавать у них иллюзию одновременного использования устройства – если каждому процессу выделен свой буфер.
- Метод моделирования не связан с реальными устройствами вообще. Устройство моделируется ОС чисто программными методами. Естественным применением этого метода является отработка приемов работы с устройствами, отсутствующими в конфигурации данной вычислительной системы. Часто ОС удобно представлять некоторые свои ресурсы как метафоры (подобия) устройств – это также моделируемые устройства.
Интерфейсы устройств
При всем многообразии внешних устройств ЭВМ и способов управления ими их программные интерфейсы могут быть сведены к трем основным моделям, определяющимся способом подключения устройств к ЭВМ:
- Регистры устройств. Устройство может иметь большое число регистров, которые, однако, сводятся к трем основным типам: регистры состояния, регистры управления, регистры данных. Регистры устройств являются расширением адресного пространства ЭВМ. Расширение это может быть как явным – с доступом при помощи команд работы с памятью типа MOV, так и неявным – с отдельной адресацией портов ввода-вывода и доступом при помощи специальных команд типа IN/OUT. Сложные по управлению устройства подсоединяются к ЭВМ через контроллеры ввода-вывода, один контроллер может обслуживать несколько однотипных устройств. С точки зрения программного интерфейса регистры контроллера выглядят для программы так же, как и регистры устройств.
- Прямой доступ к памяти (ПДП, DMA – direct memory access). Контроллер ПДП работает параллельно с центральным процессором и обменивается данными прямо с оперативной памятью, минуя центральный ЦП. Сам контроллер ПДП выглядит для программы как устройство с доступом через регистры. Программа, которая инициирует прямой обмен должна его запрограммировать. Об окончании обмена программа может узнать либо по прерыванию, либо опрашивая регистр состояния контроллера. Контроллер ПДП обычно содержит собственную буферную память для сглаживания разницы в быстродействии устройства и оперативной памяти.
- Каналы ввода-вывода. Каналы являются универсальными процессорами ввода-вывода, к одному каналу могут быть одновременно подсоединены контроллеры разных устройств. Работа канала во многом похожа на работу контроллера ПДП: канал программируется, а затем запускается операция, в ходе которой канал обеспечивает прямой обмен с оперативной памятью, минуя ЦП. Но программирование канала состоит в передаче ему канальной программы, которая размещается в оперативной памяти. Получив свою программу, канал далее выполняет ее самостоятельно, сигнализируя прерываниями об окончании операций или об особых ситуациях. Аппаратура каналов применяется в мейнфреймах System/390.
- Процессоры ввода-вывода – универсальные процессоры с собственным ПО (включая ОС), выполняющие функции контроллера ввода-вывода. Интерфейс процессора ввода-вывода не похож на интерфейс устройства в обычном понимании. Взаимодействие ОС с процессором ввода-вывода происходит через механизм обмена сообщениями, поддерживаемый микроядром. Данные могут передаваться как в составе сообщения, так и выбираться процессором ввода-вывода непосредственно из оперативной памяти. Впервые процессоры ввода-вывода были применены в суперкомпьютерах, но с тех пор они перестали быть прерогативой суперсистем и постепенно внедряются в системы ординарные (например, в AS/400).
Драйверы устройств
Модули ОС, которые осуществляют трансляцию однотипных для всех устройств обращений к ним из процессов и из других модулей ОС в специфические для устройства управляющие воздействия и управляют выполнением этих воздействий, называются драйверами. Каждому типу устройства соответствует свой драйвер. Драйвер устройства имеет два основных уровня. Первый (верхний) уровень принимает системные вызовы от процессов и формирует на основании каждого вызова запрос. Этот же уровень выстраивает запросы в очередь и поддерживает упорядоченность этой очереди в соответствии с принятой дисциплиной обслуживания. Второй (нижний) уровень драйвера выбирает из очереди первый запрос и обслуживает его: формирует управляющие воздействия и передает их на устройство, обрабатывает прерывания от устройства и сообщает ядру ОС о наступлении событий, связанных с вводом-выводом.
Логически являясь частью ОС, драйверы, тем не менее, оформляются как отдельные модули. Поскольку каждый драйвер однозначно связан с устройством определенного типа (а возможно, и данной модификации), то и состав набора драйверов зависит от конфигурации аппаратных средств. Кроме того, как правило, должна быть обеспечиваться возможность подключения к системе новых внешних устройств без внесения изменений в ОС. При модульности драйверов это достигается простым добавлением нового драйвера к системному ПО. Драйверы загружаются в память либо при загрузке системы, либо (реже) – динамически, при возникновении потребности в них. Выбор драйверов для загрузки выполняется либо по явным указаниям в процедуре инициализации ОС, либо неявно – по имеющимся таблицам конфигурации системы, либо полностью автоматически – путем опроса при загрузке всех установленных устройств, опознания их и подключения соответствующих драйверов. Последний принцип получил название plug and play.
Модификация данных. Потоки
В целом ряде случаев данные, передаваемые из процесса на устройство или в обратном направлении, должны быть дополнительно преобразованы. Примерами таких преобразований могут быть: сжатие данных; криптографическое кодирование данных; кодирование данных, повышающее надежность из передачи и хранения; согласование с форматами сетевых протоколов; и т.д. Выполнение этих функций может быть заложено непосредственно в драйвер устройства. Однако, такой вариант снижает мобильность драйвера.
Решение этой проблемы содержится в идее потоков (Unix). Поток представляет собой цепочку очередей, через которые проходят данные, передаваемые от процесса драйверу устройства или в обратном направлении. Каждая очередь в этой цепочке обрабатывается одним программным модулем – модулем потока. Данные движутся по цепочке очередей от процесса к драйверу или обратно, по пути проходя обработку в модулях потока. Модуль потока выбирает данные из своей очереди и выполняет их обработку и передает данные в очередь к следующему модулю потока. Когда модуль завершает обработку очередной порцию информации, он запускает на выполнение следующий модуль, который обрабатывает эти данные или заносит их в свою очередь. Вставляя в поток новые модули (и, соответственно, новые очереди) можно обеспечивать сколь угодно сложную дополнительную обработку данных. Потоки прозрачны для пользовательских процессов, но последним предоставляется возможность вставлять в потоки собственные модули.
Буферизация
Хотя блокировки и не влияют на результат выполнения процесса и не отражаются на его виртуальном времени, они сказываются на реальном времени его выполнения. Для интерактивных процессов они могут стать основным фактором, определяющим время реакции процесса. Невыгодны блокировки и для ОС, так как каждая блокировка – это переключение процессов, а следовательно, накладные расходы. Одним из способов, позволяющим избежать блокировок (или, по крайней мере, уменьшить их количество) является буферизация данных. Для устройства ввода-вывода назначается буферная область в оперативной памяти. Обмен данными происходит между процессом и буферной областью, а обмен между буферной областью и устройством выполняет ОС независимо от выполнения процесса. Если, например, выполнение системного вызова write будет включать в себя только пересылку данных в оперативной памяти, то блокировка процесса на время выполнения этого вызова не нужна. Буферизация, таким образом, сглаживает различия в скоростях работы производителя и потребителя информации и позволяет избежать излишних блокировок. Особенно существенный эффект буферизация может дать при последовательном вводе-выводе, так как при вводе ОС может предсказать, какой блок информации понадобится следующим и произвести его упреждающее чтение в буфер. Еще один полезный эффект буферизации – ОС имеет возможность сгруппировать данные в буфере и производить обмен с внешним устройством большими блоками.
В зависимости от целевой направленности ОС, она может обеспечивать буферизацию сама (например, через потоки) или предоставлять эту функцию процессам.