Назначение и типы прерываний

 

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

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

В зависимости от источника прерывания делятся на три больших класса:

- внешние;

- внутренние;

- программные.

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

Внутренние прерывания, называемые также исключениями (exception), происхо­дят синхронно выполнению программы при появлении аварийной ситуации в ходе исполнения некоторой инструкции программы. Примерами исключений явля­ются деление на нуль, ошибки защиты памяти, обращения по несуществующему адресу, попытка выполнить привилегированную инструкцию в пользовательском режиме и т. п. Исключения возникают непосредственно в ходе выполнения так­тов команды (“внутри” выполнения).

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

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

Прерывания обычно обрабатываются модулями операционной системы, так как действия, выполняемые по прерыванию, относятся к управлению разделяемыми ресурсами вычислительной системы – принтером, диском, таймером, процессо­ром и т. п. Процедуры, вызываемые по прерываниям, обычно называют обработ­чиками прерываний, или процедурами обслуживания прерываний (Interrupt Service Routine, ISR). Аппаратные прерывания обрабатываются драйверами соответствую­щих внешних устройств, исключения – специальными модулями ядра, а про­граммные прерывания – процедурами ОС, обслуживающими системные вызовы. Кроме этих модулей, в операционной системе может находиться так называемый диспетчер прерываний, который координирует работу отдельных обработчиков прерываний.

 

Механизм прерываний

 

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

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

Устройствам, которые используют векторные прерывания, назначается вектор прерываний. Он представляет собой электрический сигнал, выставляемый на со­ответствующие шины процессора и несущий в себе информацию об определен­ном, закрепленном за данным устройством номере, который идентифицирует со­ответствующий обработчик прерываний. Этот вектор может быть фиксированным, конфигурируемым (например, с использованием переключателей) или програм­мируемым. Операционная система может предусматривать процедуру регистра­ции вектора обработки прерываний для определенного устройства, которая свя­зывает некоторую подпрограмму обработки прерываний с определенным вектором. При получении сигнала запроса прерывания процессор выполняет специальный цикл подтверждения прерывания, в котором устройство должно идентифициро­вать себя. В течение этого цикла устройство отвечает, выставляя на шину вектор прерываний. Затем процессор использует этот вектор для нахождения обработ­чика данного прерывания. Примером шины подключения внешних устройств, кото­рая поддерживает векторные прерывания, является шина VMEbus.

При использовании опрашиваемых прерываний процессор получает от запросив­шего прерывание устройства только информацию об уровне приоритета преры­вания (например, номере IRQ на шине ISA или номере IPL на шине SBus компью­теров SPARC). С каждым уровнем прерываний может быть связано несколько устройств и соответственно несколько программ – обработчиков прерываний. При возникновении прерывания процессор должен определить, какое устройст­во из тех, которые связаны с данным уровнем прерываний, действительно запро­сило прерывание. Это достигается вызовом всех обработчиков прерываний для данного уровня приоритета, пока один из обработчиков не подтвердит, что пре­рывание пришло от обслуживаемого им устройства. Если же с каждым уровнем прерываний связано только одно устройство, то определение нужной программы обработки прерывания происходит немедленно, как и при векторном прерывании. Опрашиваемые прерывания поддерживают шины ISA, EISA, MCA, PCI и Sous.

Механизм прерываний некоторой аппаратной платформы может сочетать век­торный и опрашиваемый типы прерываний. Типичным примером такой реализа­ции является платформа персональных компьютеров на основе процессоров Intel Pentium. Шины PCI, ISA, EISA или MCA, используемые в этой платформе в ка­честве шин подключения внешних устройств, поддерживают механизм опра­шиваемых прерываний. Контроллеры периферийных устройств выставляют на шину не вектор, а сигнал запроса прерывания определенного уровня IRQ. Одна­ко в процессоре Pentium система прерываний является векторной. Вектор пре­рываний в процессор Pentium поставляет контроллер прерываний, который ото­бражает поступающий от шины сигнал IRQ на определенный номер вектора. Вектор прерываний, передаваемый в процессор, представляет собой целое число в диапазоне от 0 до 255, указывающее на одну из 256 программ обработки пре­рываний, адреса которых хранятся в таблице обработчиков прерываний. В том случае, когда к каждой линии IRQ подключается только одно устройство, проце­дура обработки прерываний работает так, как если бы система прерываний была чисто векторной, т.е. процедура не выполняет никаких дополнительных опросов для выяснения того, какое именно устройство запросило прерывание. Однако при совместном использовании одного уровня IRQ несколькими уст­ройствами программа обработки прерываний должна работать в соответствии со схемой опрашиваемых прерываний, т.е. дополнительно выполнить опрос всех устройств, подключенных к данному уровню IRQ.

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

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

Упорядоченное обслуживание запросов прерываний наряду со схемами приори­тетной обработки запросов может выполняться механизмом маскирования запро­сов. Собственно говоря, в описанной схеме абсолютных приоритетов выполняет­ся маскирование – при обслуживании некоторого запроса все запросы с равным или более низким приоритетом маскируются, т.е. не обслуживаются. Схема маскирования предполагает возможность временного маскирования прерываний любого класса независимо от уровня приоритета.

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

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

2. Автоматически сохраняется некоторая часть контекста прерванного потока, которая позволит ядру возобновить исполнение потока процесса после обра­ботки прерывания. В это подмножество обычно включаются значения счетчи­ка команд, слова состояния машины, хранящего признаки основных режимов работы процессора (пример такого слова – регистр EFLAGS в Intel Pentium), а также нескольких регистров общего назначения, которые требуются про­грамме обработки прерывания. Может быть сохранен и полный контекст про­цесса, если ОС обслуживает данное прерывание со сменой процесса. Однако в общем случае это не обязательно, часто обработка прерываний выполняется без вытеснения текущего процесса. Решение о перепланировании процессов может быть принято в ходе обработки прерыва­ния, например, если это прерывание от таймера, и после наращивания значения систем­ных часов выясняется, что процесс исчерпал выделенный ему квант времени. Однако это совсем не обязательно – прерывание может выполняться и без смены процесса, напри­мер прием очередной порции данных от контроллера внешнего устройства чаще всего происходит в рамках текущего процесса, хотя данные, скорее всего, предназначены дру­гому процессу.

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

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

5. После того как прерывание обработано ядром операционной системы, пре­рванный контекст восстанавливается и работа потока возобновляется с пре­рванного места. Часть контекста восстанавливается аппаратно по команде возврата из прерываний (например, адрес следующей команды и слово со­стояния машины), а часть – программным способом, с помощью явных ко­манд извлечения данных из стека. При возврате из прерывания блокировка повторных прерываний данного типа снимается.

Программное прерывание реализует один из способов перехода на подпрограм­му с помощью специальной инструкции процессора, такой как INT в процессо­рах Intel Pentium, trap в процессорах Motorola, syscal1 в процессорах MIPS или Ticc в процессорах SPARC. При выполнении команды программного прерывания процессор отрабатывает ту же последовательность действий, что и при возник­новении внешнего или внутреннего прерывания, но только происходит это в предсказуемой точке программы – там, где программист поместил данную ко­манду.

Практически все современные процессоры имеют в системе команд инструкции программных прерываний. Одной из причин появления инструкций программ­ных прерываний в системе команд процессоров является то, что их использова­ние часто приводит к более компактному коду программ по сравнению с исполь­зованием стандартных команд выполнения процедур. Это объясняется тем, что разработчики процессора обычно резервируют для обработки прерываний не­большое число возможных подпрограмм, так что длина операнда в команде про­граммного прерывания, который указывает на нужную подпрограмму, меньше, чем в команде перехода на подпрограмму. Например, в процессоре х86 преду­смотрена возможность применения 256 программ обработки прерываний, поэто­му в инструкции INT операнд имеет длину один байт (а инструкция INT 3, кото­рая предназначена для вызова отладчика, вся имеет длину один байт). Значение операнда команды INT просто является индексом в таблице из 256 адресов под­программ обработки прерываний, один из которых и используется для перехода по команде INT. При использовании команды CALL потребовался бы уже не одно­байтовый, а двух- или четырехбайтовый операнд. Другой причиной применения программных прерываний вместо обычных инструкций вызова подпрограмм является возможность смены пользовательского режима на привилегированный одновременно с вызовом процедуры – это свойство программных прерываний поддерживается большинством процессоров.

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

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

Операционная система не может терять контроль над ходом выполнения систем­ных процедур, вызываемых по прерываниям. Она должна упорядочивать их во времени так же, как планировщик упорядочивает многочисленные пользователь­ские потоки. Кроме того, сам планировщик потоков является системной проце­дурой, вызываемой по прерываниям (аппаратным – от таймера или контроллера устройства ввода-вывода, или программным – от приложения или модуля ОС). Поэтому правильное планирование процедур, вызываемых по прерываниям, является необходимым условием правильного планирования пользовательских потоков. В противном случае в системе могут возникать, например, такие ситуа­ции, когда операционная система будет длительное время заниматься не требую­щей мгновенной реакции задачей управления стримером, архивирующим дан­ные, в то время, когда высокоскоростной диск будет простаивать и тормозить работу многочисленных приложений, обменивающихся данными с этим диском. Еще один пример такой ситуации иллюстрирует рис. 12. В данном случае обра­ботчик прерываний принтера блокирует на длительное время обработку преры­вания от таймера, в результате чего системное время на некоторое время “зами­рает” и поток 2, критически важный для пользователя, не получает управление в запланированное время. Остроту проблемы несколько смягчает то обстоятельст­во, что во многих случаях обработка прерывания связана с выполнением всего нескольких операций ввода-вывода и поэтому имеет очень небольшую продол­жительность. Тем не менее ОС всегда должна контролировать ситуацию и вы­полнять критичную работу вовремя, а не полагаться на волю случая.

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

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

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

Некоторые процессоры или контроллеры прерываний компьютера на аппарат­ном уровне поддерживают приоритезацию запросов на прерывание. Например, в процессорах MIPS существует несколько уровней аппаратных запросов на прерывания и несколько уровней программных запросов. В процессоре имеется внутренняя переменная, называемая уровнем прерываний процессора. Прерыва­ние происходит только в том случае, когда уровень запроса на прерывание выше текущего уровня прерываний процессора. Имеется также привилегированная инструкция, с помощью которой код ядра ОС может изменить уровень прерыва­ний процессора. Необработанные запросы на прерывания должны храниться в контроллерах устройств, чтобы не потеряться и дождаться обслуживания при снижении уровня прерывания процессора, когда он закончит выполнение более срочных работ. При работе на такого рода аппаратной платформе ОС может поль­зоваться встроенными в процессор средствами приоритезации прерываний для упорядочивания процесса их обработки. Однако такие средства имеются не у всех процессоров, например процессоры семейства Pentium не имеют встроенной пе­ременной для фиксации уровня прерываний исполняемого кода – аппаратные прерывания могут быть либо полностью запрещены, либо полностью разрешены (а наиболее критичные нельзя запретить вовсе).

Для исключения зависимости от аппаратной платформы в некоторых ОС вво­дится собственная программная система приоритетов прерываний. Примером та­кой ОС может служить Windows NT.

Диспетчер прерываний Windows NT (так называемый Trap Handler) работает с программной моделью прерываний, единой для всех аппаратных платформ, поддерживаемых Windows NT. Все источники прерываний (аппаратных и про­граммных, а также некоторых важных для системы исключений, например ис­ключения по ошибке шины) делятся на несколько классов, и каждому классу присваивается уровень запроса прерывания – Interrupt Request Level (IRQL). Этот уровень и представляет приоритет данного класса. Операционная система про­граммным способом поддерживает внутреннюю переменную, называемую IRQL выполняемого процессором кода, которая по назначению соответствует уровню прерывания процессора. Если процессор, на котором работает ОС, поддерживает такую переменную, то она используется и IRQL выполняемого кода отображает­ся на нее, в противном случае соответствующие функции процессора эмулиру­ются программно.

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

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

Таким образом, запрос на прерывание принимается диспетчером прерываний всегда независимо от текущего уровня IRQL выполняемого кода, но диспетчер прерываний не передает его на обработку соответствующей процедуре обработ­ки прерываний, а помещает в программную очередь запросов, если в данный момент выполняется более приоритетная процедура обработки прерываний. Операционная система имеет полный контроль над ситуацией, не позволяя контроллерам устройств ввода-вывода принимать решения о ходе вычислительного процесса.

В Windows NT низший уровень IRQL соответствует обычным потокам, назна­чаемым на выполнение диспетчером потоков (рис. 13). Это является некото­рым допущением, так как код потоков начинает выполняться процессором не в результате запроса на прерывание. Однако это допущение хорошо работает, посколь­ку позволяет любому “настоящему” запросу прерывать код обычного потока.

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

Прерывания от внешних устройств занимают промежуточные уровни IRQL. Конкретное соотношение между приоритетами внешних устройств определяется приоритетами, задаваемыми аппаратной платформой, например уровнем IRQ ши­ны PCI, назначенным устройству.

Особую роль в работе вычислительной системы играет системный таймер: на основании его прерываний обновляются системные часы, определяющие очеред­ной момент вызова планировщика потоков, момент выдачи управляющего воз­действия потоком реального времени и многое другое. Ввиду важности немед­ленной обработки прерываний от таймера, ему в Windows NT дан весьма высокий уровень приоритета – более высокий, чем уровень любого устройства ввода-вы­вода.

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

Программные прерывания, обслуживающие системные вызовы от приложений, выполняются с низшим уровнем приоритета, что соответствует концепции про­должения одного и того же процесса, но только в системной фазе, при выполне­нии системного вызова. А вот для программных прерываний, исходящих от мо­дулей ядра ОС, отводится более высокий уровень запросов, имеющий двойное название “диспетчерский/DPC”. Этот уровень приоритета называется диспетчерским, потому что именно в эту очередь помещаются программные запросы, вызывающие диспетчер потоков. Часто при обработке высокоприоритетных прерываний возникает ситуация, требующая перепланирования потоков. Например, при обработке очередного прерывания от таймера нужно проверить, не исчерпан ли квант, выделенный текущему потоку. Другим примером может служить обработка прерывания от контроллера диска после завершения дисковой операции, которую могут ждать несколько потоков. Во всех таких ситуациях в Windows NT планировщик/диспетчер вызывается высокоуровневыми процедурами ядра не прямо посредством вызова процедуры, а косвенно, с помощью программного прерывания. Это дает возможность отде­лить короткую, но требующую быстрой реакции системы процедуру обслужи­вания высокоприоритетного прерывания (например, наращивание системных часов), от менее критичной операции перепланирования пользовательских пото­ков. Прямой вызов планировщика/диспетчера потоков такой возможности бы не дал, и критичные запросы прерывания от контроллеров устройств ввода-вывода вынуждены были бы ждать, пока отработает планировщик. При этом планиров­щик, возможно, выбрал бы для выполнения другой поток, если бы работал после процедур обслуживания аппаратных прерываний, так как он получил бы свежие сведения о завершении некоторых операций ввода-вывода. Помещение вызова планировщика потоков в очередь позволяет выполнять его только в тех ситуаци­ях, когда в системе отсутствуют ожидающие аппаратные запросы прерываний.

Наличие отдельного уровня для планировщика/диспетчера потоков не означа­ет того, что он всегда вызывается с помощью программных прерываний. В тех случаях, когда он вызывается из кода, имеющего низкий уровень запроса на пре­рывание (если он выполняется, значит, высокоприоритетные запросы на преры­вание отсутствуют), планировщик может быть вызван быстрее путем непосред­ственного внутрисегментного вызова процедуры. Например, системному вызову, переводящему поток по собственному желанию в состояние ожидания, нет смыс­ла вызывать планировщик потоков по программному прерыванию.

Второе название диспетчерского уровня – DPC (Deffered Procedure Callвызов отложенной процедуры), говорит о том, что на этом уровне ожидают своей очереди отложенные вызовы и других процедур ОС, а не только планировщика/диспетчера. Процедуры ОС могут вызывать друг друга и непосредственно, но при многослойном построении ядра существуют более и ме­нее приоритетные процедуры, и вызов менее приоритетных процедур из более приоритетных с помощью механизма программных прерываний позволяет, как и в случае с планировщиком, упорядочить во времени их выполнение, что оптими­зирует работу ОС в целом. Примером процедур ОС, работающих на высоком приоритетном уровне, являются те части драйверов устройств ввода-вывода, ко­торые выполняют короткие, но критичные ко времени реакции действия. В то же время существуют и другие части драйверов, которые выполняют менее сроч­ную, но более объемную работу. В операционных системах семейства UNIX эти части называют соответственно верхни­ми половинами (top half) и нижними половинами (bottom half) обработчика прерыва­ний. В Windows NT такие части драйверов оформляют как процедуры, вызываемые с помощью программных прерываний уровня “диспетчерский/DPC” (DPC-процедуры), а само программное прерывание вы­полняет критичная часть драйвера. Естественно, существуют и другие модули ОС, оформляемые подобным образом.

Описанная программная реализация приоритетного обслуживания прерываний приводит к однотипной работе ОС Windows NT на различных аппаратных плат­формах, что упрощает логику работы ОС и ее перенос на новые платформы. От­рицательным следствием такого централизованного подхода является некоторое замедление обработки прерываний, так как вместо непосредственной передачи управления драйверу устройства или обработчику исключений выполняется вызов некоего посредника – диспетчера прерываний. В ОС семейства UNIX принят похожий, но менее централизованный подход к ведению и обработке очередей прерываний. Вместо единого диспетчера прерываний его функции выполняют процедуры, обслуживающие каждый приоритетный класс прерываний. Тем не ме­нее, общий подход к упорядочиванию обработки прерываний за счет их много­уровневой приоритезации и ведения системы очередей присутствует практически во всех современных операционных системах.

Важной особенностью процедур, выполняемых по запросам прерываний, являет­ся то, что они выполняют работу, чаще всего никак не связанную с текущим про­цессом. Например, драйвер диска может получить управление после того, как контроллер диска записал в соответствующие сектора информацию, полученную от процесса А, но этот момент времени, скорее всего, не совпадет с периодом оче­редной итерации выполнения процесса А или его потока. В наиболее типичном случае процесс А будет находиться в состоянии ожидания завершения операции ввода-вывода (при синхронном режиме выполнения этой операции) и драйвер диска прервет какой-либо другой процесс, например процесс В. В Windows NT процедуры, вызываемые как DPC, также могут работать в контексте процесса, отличающегося от того, для которого они выполняют свои функции. В некото­рых случаях вообще трудно однозначно определить, для какого процесса выпол­няет работу тот или иной программный модуль ОС, например планировщик по­токов. Поэтому для такого рода процедур вводятся ограничения – они не имеют права использовать ресурсы (память, открытые файлы и т.п.), с которыми рабо­тает текущий процесс, или же от имени этого процесса запрашивать выделение дополнительных ресурсов. Процедуры обработки прерываний работают с ресур­сами, которые были выделены им при инициализации соответствующего драй­вера или инициализации самой операционной системы. Эти ресурсы принад­лежат операционной системе, а не конкретному процессу. В частности, память выделяется драйверам из системной области, т.е. той области, на которую отображаются сегменты из общей части виртуального адресного пространства всех процессов. Поэтому обычно говорят, что процедуры обработки прерываний работают вне контекста процесса. Поскольку все подобные процедуры являются частью операционной системы, ответственность за соблюдение этих ограничений несет системный программист. Заставить свои модули выполнять эти огра­ничения ОС не может.

Хороший пример того, что не бывает правил без исключений, предоставляет нам ОС Windows NT. В ней существуют процедуры обработки прерываний, которые выполняются всегда в контексте определенного процесса. Это процедуры, вызы­ваемые с помощью программного прерывания АРС (Asynchronous Procedure Call – вызов асинхронной процедуры). Для них в диспетчере прерываний предусмот­рен свой уровень приоритета IRQL, выше уровня для обычного кода, но ниже уровня DPC. Эти процедуры могут прервать текущий код и выполниться при соблюдении двух условий: текущий код имеет низший уровень приоритета (т.е. выполняется обычный код), текущим процессом является вполне опреде­ленный процесс, описатель которого был задан в запросе на прерывание для дан­ной процедуры АРС. Процедуры АРС могут пользоваться ресурсами текущего процесса, для чего они и были введены. Основное назначение АРС-процедур – перемещение данных, полученных драйвером от какого-либо устройства ввода-вывода, из памяти системной области памяти, куда они поме­щаются после считывания из регистров контроллера этого устройства, в индиви­дуальную часть адресного пространства процесса, запросившего операцию вво­да-вывода. Такое действие постоянно выполняется системой ввода-вывода, и для его реализации были введены такие специфические процедуры обработки пре­рываний, как АРС.

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

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

Реализация системных вызовов должна удовлетворять следующим требованиям:

- обеспечивать переключение в привилегированный режим;

- обладать высокой скоростью вызова процедур ОС;

- обеспечивать по возможности единообразное обращение к системным вызо­вам для всех аппаратных платформ, на которых работает ОС;

- допускать легкое расширение набора системных вызовов;

- обеспечивать контроль со стороны ОС за корректным использованием сис­темных вызовов.

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

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

В большинстве ОС системные вызовы обслуживаются по централизованной схе­ме, основанной на существовании диспетчера системных вызовов (рис. 14, б). При любом системном вызове приложение выполняет программное прерывание с определенным и единственным номером вектора. Например, ОС Linux исполь­зует для системных вызовов команду INT 80h, а ОС Windows NT (при работе на платформе Pentium) – INT 2Eh. Перед выполнением программного прерывания приложение тем или иным способом передает операционной системе номер сис­темного вызова, который является индексом в таблице адресов процедур ОС, реализующих системные вызовы (таблица sysent на рис. 14). Способ передачи зависит от реализации, например номер можно поместить в определенный ре­гистр общего назначения процессора или передать через стек (в этом случае после прерывания и перехода в привилегированный режим их нужно будет скопиро­вать в системный стек из пользовательского, это действие в некоторых процес­сорах автоматизировано). Также некоторым способом передаются аргументы системного вызова, они могут как помещаться в регистры общего назначения, так и передаваться через стек или массив, находящийся в оперативной памяти. Массив удобен при большом объеме данных, передаваемых в качестве аргумен­тов, при этом в регистре общего назначения указывается адрес этого массива.

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

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

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

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

Для ускорения выполнения некоторых достаточно простых системных вызовов, которым к тому же не требуется работа в привилегированном режиме, требуемая работа полностью выполняется библиотечной функцией, которую несправедли­во называть в данном случае заглушкой. Точнее, такая функция не являет­ся системным вызовом, а представляет собой “чистую” библиотечную функцию, выполняющую всю свою работу в пользовательском режиме в виртуальном ад­ресном пространстве процесса, но прикладной программист может об этом и не знать – для него системные вызовы и библиотечные функции выглядят едино­образно. Прикладной программист имеет дело с набором функций прикладного программного интерфейса – API (например, Win32 или POSIX), – состоящего и из библиотечных функций, часть из которых пользуется для завершения рабо­ты системными вызовами, а часть – нет.

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

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

Асинхронный системный вызов не приводит к переводу процесса в режим ожида­ния после выполнения некоторых начальных системных действий, например за­пуска операции вывода-вывода, управление возвращается прикладному процес­су (рис. 15, б).

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