Меню

Что такое системные средства взаимодействия

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

Проблемы параллельного выполнения

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

Основной задачей управления параллельным выполнением является задача взаимного исключения: два процесса не могут выполнять одновременный доступ к разделяемым ресурсам.

В некоторых случаях возникает необходимость в приостановке выполнения процесса до наступления некоторого внешнего по отношению к нему события — такая задача называется задачей синхронизации. Если рассматривать события как ресурсы (потребляемые), то задача синхронизации является частным случаем задачи взаимного исключения. Как частный случай, она допускает частные решения.

Ниже рассматриваются средства, которые предоставляются для решения этих задач практически всеми современными ОС.

Критической секцией называется такой участок кода процесса, во время выполнения которого процесс не может быть прерван. Участок кода, на котором процесс осуществляет доступ к совместно используемому ресурсу, берется в «скобки критической секции». На время прохождения процесса через критическую секцию все остальные процессы блокируются, что снижает уровень мультипрограммирования в системе. Поэтому это средство широко применяются только для потоков — параллельно выполняющихся частей одного и того же процесса: они все принадлежат одному процессу — одному пользователю и разделяют почти все ресурсы этого процесса. Блокировка затрагивает только другие потоки того же процесса и не затрагивает другие процессы и их потоки.

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

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

В ОС определены допустимые типы сигналов. Сигналы могут генерироваться системой автоматически в следующих случаях:

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

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

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

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

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

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

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

Общие области памяти

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

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

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

Семафор является целочисленной переменной, которая в простейшем случае (двоичный семафор) может принимать значения 0 или 1. Над семафорами определены 2 операции:

V-операция есть операция с одним операндом, который должен быть семафором. Выполнение операции состоит в увеличении значения на 1, это действие должно быть атомарным.

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

Атомарность P-операции и является потенциальной задержкой: если процесс пытается выполнить P-операцию над семафором, значение которого в данный момент нулевое, данная P-операция не может завершиться пока другой процесс не выполнит V-операцию над этим семафором. Несколько процессов могут начать одновременно P-операцию над одним и тем же семафором. Тогда при установке семафора в 1 только одна из P-операций завершится.

Защита разделяемых ресурсов теперь выглядит следующим образом. Каждый ресурс защищается своим семафором, значение которого может быть 1 — свободен или 0 — занят. Процесс, выполняющий доступ к ресурсу, инициирует P-операцию. Если ресурс занят — процесс задерживается в своей P-операции до освобождения ресурса. Когда ресурс освобождается, P-операция процесса завершается и процесс занимает ресурс. При освобождении ресурса процесс выполняет V-операцию

Для решения задачи взаимного исключения достаточно двоичных семафоров. Семафор, который может принимать неотрицательные значения, большие 1, называется общим семафором. Такой семафор может быть очень удобен, например, при управлении не единичным ресурсом, а классом ресурсов. Начальное значение семафора устанавливается равным числу единиц ресурса в классе. Каждое выделение единицы ресурса процессу сопровождается P-операцией, уменьшающей значение семафора. Семафор, таким образом, играет роль счетчика ресурсов. Когда этот счетчик достигнет нулевого значения, процесс, выдавший следующий запрос на ресурс, будет заблокирован в своей P-операции. Освобождение ресурса сопровождается V-операцией, которая разблокирует процесс, ожидающий ресурс или наращивает счетчик ресурсов.

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

Для процесса канал практически аналогичен файлу. Специальные системные вызовы используются для создания канала и получения доступа к каналу, а для работы с каналом используются те же вызовы read и write, что и для файлов, и закрытие канала выполняется вызовом close. Канал, однако, представляет собой не внешние данные, а область памяти, доступ к которой осуществляется по дисциплине FIFO — первый вошел – первый вышел. Для канала выделяется память в системной области, что может ограничивать емкость канала. В связи с этим процесс, обращающийся к каналу, может блокироваться: при попытке чтения из пустого канала и при попытке записи в переполненный канал.

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

Читайте также:  Корейские моющие средства для стирки

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

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

Как правило, возможности процесса-получателя сообщений не ограничиваются чтением по дисциплине FIFO, ему предоставляется более богатый выбор дисциплин: LIFO, по приоритету, по типам, по идентификаторам отправителя и т.п. В распоряжении владельца имеются также средства определения размера очереди, а возможно, и просмотра очереди — неразрушающего чтения из нее. В распоряжении процесса-отправителя имеется только вызов посылки сообщения в очередь.

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

Общие требования безопасности

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

В США, а де-факто и во всем мире, требования безопасности к компьютерным системам в бизнесе определяются стандартным классом C2. (Правительственные стандарты США определяют несколько классов безопасности: от самого высокого — A до самого низкого — D, с дополнительной градацией в классах B и C). Основные требования класса C2 сводятся к следующим:

владелец ресурса должен управлять доступом к ресурсу;

ОС должна защищать объекты от несанкционированного использования другими процессами (в том числе и после их удаления);

перед получением доступа к системе каждый пользователь должен идентифицировать себя, введя уникальное имя входа в систему и пароль; система должна быть способной использовать эту уникальную информацию для контроля действий пользователя;

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

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

Модель защиты рассматривает объекты — элементы системы, которым требуется защита, и субъекты — активные элементы, стремящиеся получить доступ к объектам. Механизмы защиты должны обеспечивать ограничение доступа субъектов к объектам: во-первых, доступ к объекту должен быть разрешен только для определенных субъектов, во-вторых, даже имеющему доступ субъекту должно быть разрешено выполнение только определенного набора операций. Для обеспечения защиты могут применяться: кодирование объектов; сокрытие местоположения объектов; инкапсуляция объектов. Для каждого объекта определено множество допустимых для него операций. Применительно к файлам, например, возможны в общем случае следующие операции:

получение информации из объекта;

обновление информации в объекте;

добавление в объект новой информации (без изменения старой);

интерпретация объекта как исполняемого кода;

получение информации об объекте;

установка информации об объекте;

установка прав доступа к объекту.

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

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

Реализация системы защиты

Простейшим примером реализации защиты является защита файлов в традиционной ФС s5 ОС Unix. В дескрипторе каждого файла хранится идентификатор владельца файла и битовые тройки вида rwx (читать, писать, выполнять). Первая тройка определяет права доступа к файлу его владельца, вторая – пользователей, входящих в ту же группу, что и владелец, третья – всех остальных пользователей.

В наиболее последовательном виде объектная модель доступа м.б. реализована в объектно-ориентированных системах. Во-первых, в таких системах объектами являются не только файлы, но и любые ресурсы. Доступ к любому ресурсу сопровождается процедурой проверки прав доступа – одинаковой для любого типа объектов. Инкапсуляция и определение методов для объектов просто не дают возможности выполнить над объектом непредусмотренную для него операцию.

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

Связанную с пользователем информацию, относящуюся к системе защиты, называют бюджетом. Дескриптор пользователя в ОС обычно называют профилем пользователя. Помимо бюджетной информации в профиль могут входить настройки интерфейса рабочего места, процедура, автоматически выполняемая при начале пользователем сеанса, установки библиотек и каталогов для поиска и т.д. и т.п. Независимо от того, используются в системе списки возможностей или списки управления доступом, некоторые данные бюджета все равно содержатся в профиле пользователя: его имя и пароль (последний — обычно в закодированном виде), его класс, список групп, к которым он принадлежит. Бюджеты пользователей должны быть одними из наиболее строго защищаемых объектов в системе.

Читайте также:  Акционерный капитал как источник средств компании

Источник



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

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

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

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

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

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

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

raiseInterrupt (pid, intType );

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

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

(intType, action, procedure );

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Читайте также:  Средства индивидуальной безопасности характеристика

Источник

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

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

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

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

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

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

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

raiseInterrupt (pid, intType );

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

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

(intType, action, procedure );

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Источник