Студопедия

Главная страница Случайная страница

Разделы сайта

АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторикаСоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника






Распределение исключений






В отличие от прерываний, которые могут возникать непредсказуемо, исключе­ния — это прямой результат выполнения программы. Microsoft С определяет архитектуру программирования, известную как структурная обработка исклю­чений, которая обеспечивает приложениям унифицированную реакцию на ис­ключения. Все исключения, кроме тех, которые достаточно просты и обрабатывают­ся непосредственно обработчиком ловушки, обслуживаются модулем ядра, дис­петчером исключений (exception dispatcher) (см. рис. 7-6). Этот модуль зависит от архитектуры процессора, но написан на С. Задача диспетчера исключений состоит в том, чтобы найти обработчик исключений, который может данное исключение " устранить". Ниже приведен список машинно-независимых исклю­чений, определенных ядром:

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

 

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

Некоторым исключениям позволено " нетронутыми" выходить обратно в пользовательский режим. Например, нарушение защиты памяти или арифмети­ческое переполнение генерируют исключения, которые ОС не обрабатывает. Для таких исключений подсистема среды или приложение могут задать при помощи специальных конструкций языка высокого уровня блочные обработчики ис­ключений (frame based exception handlers). Microsoft С — это первый компилятор Microsoft, поддерживающий структурную обработку исключений, однако сред­ства обработки исключений Windows NT не зависят от языка программирования.

Термин блочный обозначает связь обработчика исключений с активизаци­ей определенной процедуры. При вызове процедуры в стеке создается стековый фрейм, представляющий данную активизацию процедуры. Со стековым фрей­мом может быть связан один или несколько обработчиков исключений, каждый из которых защищает какой-либо блок исходного кода программы. Когда воз­никает исключение, ядро ищет обработчик, связанный с текущим стековым фреймом. Если его нет, то выполняется поиск обработчика для предыдущего фрейма и так далее, пока блочный обработчик исключений не будет найден. Если найти обработчик не удается, то ядро вызывает собственные обработчики исключений по умолчанию. (Обратите внимание, что обработка исключении реализована по-разному для разных процессоров. Реализация для Intel х8б при­меняет подход, использующий стековые фреймы, в то время как реализация для MIPS R4000 — табличный метод.)

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

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

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

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

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

исключение

рис. 7-10. Распределение исключения.

 

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

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

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

 

Распределение вызовов системных сервисов

Как показано на рис.7-6, обработчик ловушки ядра NT распределяет прерывания, исключения и вызов системных сервисов. Вызовы системных сервисов, генерирующие ловушки, которые в NT рассматриваются как исключения, интересны с точки зрения расширяемости системы. Способ реализации системных сервисов ядром позволяет динамически добавлять к ОС новые сервисы в будущих версиях.

Всякий раз, когда поток пользовательского режима вызывает системный сервис, ему вдруг разрешается выполнять привилегированный код ОС – это беда ОС. Поток пользовательского режима может “залезть” в структуры данных ОС или перепутать содержимое памяти, повредив таким образом, самой системе и другим пользователям. По этой причине процессоры обычно предоставляют специальную команду только для системных сервисов. Эта команда — syscall па процессорах MIPS и 1NT 2Eh на Intel x86 — применяется если поток пользовательского режима вызывает системный сервис. Аппаратура генерирует ловушку и переключается из пользовательского режима в режим ядра. Когда это происходит, ядро копирует значения параметров сервиса из сте­ка пользовательского режима в стек режима ядра потока (чтобы пользователь никак не мог их изменить) и затем выполняет системный сервис.

Как показано на рис. 7-11, для поиска системных сервисов ядро использу­ет таблицу распределения системных сервисов. Эта таблица похожа на таблицу распределения прерываний, описанную выше, только каждый элемент ее содер­жит указатель на системный сервис, а не на процедуру обработки.

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

Рис. 7-11. Исключения системных сервисов.

 

60. Многопроцессорная синхронизация

Концепция взаимного исключения (mutual exclusion) является критической при разработке ОС. Она подразумевает, что в любой момент времени доступ к данному ресурсу может иметь один и только один поток. Взаимное исключе­ние необходимо, когда ресурс самостоятельно не поддерживает совместный доступ или когда совместное использование может давать непредсказуемые результаты. Например, если два потока одновременно копируют файлы в порт принтера, результаты вывода могут перемешаться. Аналогично, если один по­ток считывает область памяти в тот момент, когда другой в нее записывает, первый поток получит непредсказуемые данные. В общем случае изменяемые ресурсы не могут использоваться совместно без определенных ограничений, тогда как для не модифицируемых ресурсов это допустимо. На рис. 7-12 пока­зано, что происходит, когда два потока, выполняющиеся на разных процессо­рах, выполняют запись в циклическую очередь.

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

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

Рис. 7-12. Некорректное совместное использование памяти.

Код на рис. 7-12 — критическая секция, в которой некорректно, без вза­имного исключения, осуществляется доступ к совместно используемым данным

Вопрос взаимного исключения, существенный для всех ОС, особенно ва­жен (и сложен) для тесно связанных (tightly-coupled) ОС с симметричной муль­типроцессорной обработкой (symmetric multiprocessing), таких как Windows NT; здесь один и тот же системный код выполняется на нескольких процессорах одновременно, совместно используя структуры данных в глобальной памяти. В Windows NT предоставление механизмов, которые системный код использует для предотвращения одновременной модификации структуры данных двумя потоками, — это задача ядра. Ядро обеспечивает примитивы взаимного исклю­чения; оно и другие компоненты исполнительной системы используют их для синхронизации доступа к глобальным структурам данных.

Синхронизация на уровне ядра

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

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

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

Механизм, используемый ядром для достижения многопроцессорного вза­имного исключения, называется спин-блокировкой (spin lock). Спин-блокировка — это механизм, связанный с глобальной структурой данных, например оче­редью DPC (как на рис. 7-1, 3).

Прежде чем войти в показанную на рисунке критическую секцию, ядро дол­жно получить спин-блокировку, связанную с защищенной очередью DPC. Если блокировка не свободна, то ядро пытается получить ее, пока это не увенчается успехом. Спин-блокировка названа так потому, что ядро (и, следовательно, про­цессор) изолировано и " вращается само по себе" ', пока не получит блокировку.

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

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

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

Рис. 7-13. Использование спин-блокировки.

 






© 2023 :: MyLektsii.ru :: Мои Лекции
Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав.
Копирование текстов разрешено только с указанием индексируемой ссылки на источник.