Скачиваний:
53
Добавлен:
02.05.2014
Размер:
258.56 Кб
Скачать

Глава 9. Системные средства взаимодействия процессов

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

9.1. Скобки критических секций.

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

Кроме того, роль таких скобок могут играть системные вызовы типа suspend и release, первый из которых приостанавливает выполнение нити, а второй – отменяет приостановку.

9.2. Виртуальные прерывания или сигналы

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

raiseInterrupt (pid, intType );

где pid – идентификатор процесса, которому посылается прерывание, intType – тип (возможно, номер) прерывания. Идентификатор процесса – это не внешнее его имя, а манипулятор, устанавливаемый для каждого запуска процесса ОС. Для того, чтобы процесс мог послать сигнал другому процессу, процесс-отправитель должен знать идентификатор процесса-получателя, то есть находиться с ним в достаточно "конфиденциальных" отношениях. Чтобы предотвратить возможность посылки непредусмотренных прерываний, могут быть введены дополнительные ограничения: разрешить посылку прерываний только от процессов-предков к потомкам или ограничить обмен прерываниями только процессами одного и того же пользователя.

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

setInterruptHandler

(intType, action, procedure );

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

Разумеется, в системе должны быть определены допустимые типы виртуальных прерываний. Виртуальные прерывания могут генерироваться в следующих случаях:

  • завершение или другое изменение статуса процесса-потомка;

  • программные ошибки (прерывания-ловушки);

  • ошибки в выполнении системных вызовов или неправильные обращения к системным вызовам;

  • терминальные воздействия (например, нажатие клавиши "Внимание" или Ctrl+Break);

  • при необходимости завершения процесса (системный вызов kill);

  • сигнал от таймера;

  • сигналы, которыми процессы обмениваются друг с другом;

  • и т.д.

Если процесс получает прерывание, для которого он не установил обработчик, то процесс должен аварийно завершиться (это устанавливаемый по умолчанию вид реакции на прерывание). Такая установка может показаться чрезмерно жесткой, но вспомните, например, какова будет реакция системы на реальное прерывание, для которого не определен его обработчик (вектор прерывания в Intel-Pentium).

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

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

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

В большинстве современных ОС (Unix, OS/2 и др.) виртуальные прерывания носят название сигналов и используются прежде всего для сигнализации о чрезвычайных событиях. Сигнальные системы конкретных ОС, как правило, не предоставляют в составе API универсального вызова типа raiseInterrupt, который позволял бы пользователю выдавать сигналы любого типа. Набор зарезервированных типов сигналов ограничен (в Unix, например, их 19, а в OS/2 – всего 7), не все из них доступны процессам и для каждого из доступных имеется собственный системный вызов. Недопустимы также незарезервированные типы сигналов. В набор включается несколько (по 3 – в упомянутых ОС) типов сигналов, зарезервированных за процессами, – эти типы и используют взаимодействующие процессы для посылки друг другу сигналов, которые они интерпретируют по предварительной договоренности.

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

Соседние файлы в папке Системное программирование и операционные системы