Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции по операционным системам1.doc
Скачиваний:
110
Добавлен:
02.05.2014
Размер:
1.27 Mб
Скачать

Функции Setjmp() и Longjmp(). Нелокальные переходы Setjmp.

jmp_but

Тип массива для сохранения информации об окружении, необходимой для последующего востановления окружения.

int setjmp (jmp_buf env);

Записывает состояние окружения в параметр типа jmp_buf для последующего использования функцией longjmp.

В случае прямого вызова макрос setjmp возвращает нулевое значение. В случае возврата как результата вызова функции longjmp, setjmp возвращает нулевое значение.

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

  • выражение условия в целом в операторах выбора цикла;

  • операнд операции проверки на равенство или отношения в случае, когда второй операнд является константным выражением, а все выражения в целом является выражением условия в операторах выбора или цикла;

  • операнд унарной операции!, если при этом все выражения в целом является выражением условия в операторах выбора или цикла;

  • оператор выражения в целом.

void longjmp (jmp_buf, int val);

Эта функция восстанавливает окружение, которое было сохранено в соответствующем объекте типа jmp_buf при последнем вызове макроса setjmp в пределах той же функции, из которой вызывается longjmp. Если такой вызов setjmp не был сделан или если функция, содержащая вызов setjmp, уже завершила свое выполнение, поведение функции longjmp не определено.

Все доступные объекты восстанавливают свои значения, которые они имели во втемя вызова longjmp, за исключением автоматических объектов, локальных для функции, из которой вызывался соответствующий макрос setjmp, и которые не определены со спецификатором volatile и были изменены между вызовами setjmp и longjmp – значения таких объектов не определены.

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

После завершения longjmp выполнение программы продолжается так, как если бы соответствующий вызов макроса setjmp возвратил значение, определяемое параметром val. Функция longjmp не может заставить макроса setjmp вернуть значение 0; если val равно 0, setjmp возвратит значение 1.

Супервизор.

Это часть управляющей программы, координирующая распределение ресурсов вычислительной системы.

Особенности:

  1. при ее вызове осуществляется проверка прав доступа к данной функции;

  2. замена контекста более глубокая, чем при вызове процедуры (изменяет все слово состояния);

  3. при некоторых вызовах не всегда возвращается в точку вызова;

  4. новый режим (контекст) определяется в зоне памяти, не доступной для пользователя.

Асинхронные методы замены контекста.

Эти методы основаны на использовании прерываний, и самое распространенное – прерывание по таймеру.

Стек – CS:IP:FL– не 4 байта, а 6вместоretнуженiret(retf)

Польз. програм. <intI>

Обл. записи пр.

Вектора прерываний

(1 Кбайт)

int I*4

J: адрес

(I-1)*4

Прерывания – это обычные подпрограммы.

Слово прерывание представляет довольно неудачную, на взгляд авторов, кальку с англоязычного термина interrupt ( существительное от глагола прервать).

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

Для примера рассмотрим организацию прерываний в машинах семейства PDP-11. Машины данной архитектуры сейчас почти не используются, но ряд архитектурных решений не потерял актуальности и поныне. В частности, подход к реализации прерываний считается классическим.

Процессоры семейства PDP-11 различают 128 типов прерываний и исключений. Каждому типу соответствует процедура - обработчик. Адреса точек входа всех процедур собраны в таблицу, называемую таблицей векторов прерываний. Эта таблица занимает 256 слов физической памяти, начиная с нулевого адреса. Каждый элемент таблицы (вектор) содержит адрес обработчика и новое слово состояния процессора. Ниже будет объяснено, для чего это сделано.

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

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

При завершении процедуры обработки вызывается команда RTI (ReTurn from Interrupt - возврат из прерывания). Эта команда выталкивает из стека адрес прерванной команды и старое слово состояния, тем самым восстанавливая приоритет процессора.

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

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

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

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

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

Слово прерывание представляет довольно неудачную, на взгляд авторов, кальку с англоязычного термина interrupt ( существительное от глагола прервать).

Прерывания, вызванные внутренними событиями, часто называют исключениями (exceptions). Мы далее будем разделять эти два понятия, то есть внешние прерывания будем называть просто прерываниями, а внутренние - исключениями. Исключения возникают при делении на ноль, неопределенном коде команды, ошибках обращения к памяти и т.д.

Реализации прерываний и исключений у разных процессоров немного отличаются.

Для примера рассмотрим организацию прерываний в машинах семейства PDP-11. Машины данной архитектуры сейчас почти не используются, но ряд архитектурных решений не потерял актуальности и поныне. В частности, подход к реализации прерываний считается классическим.

Процессоры семейства PDP-11 различают 128 типов прерываний и исключений. Каждому типу соответствует процедура - обработчик. Адреса точек входа всех процедур собраны в таблицу, называемую таблицей векторов прерываний. Эта таблица занимает 256 слов физической памяти, начиная с нулевого адреса. Каждый элемент таблицы (вектор) содержит адрес обработчика и новое слово состояния процессора. Ниже будет объяснено, для чего это сделано.

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

Каждый вход соответствует определенному уровню приоритета. PDP-11 имеет восемь уровней приоритета прерывания. Прерывание происходит только когда уровень приоритета процессора ниже приоритета запрашиваемого прерывания. Если у процессора установлен приоритет 7, внешние прерывания запрещены. Приоритет процессора задается его словом состояния.

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

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

При завершении процедуры обработки вызывается команда RTI (ReTurn from Interrupt - возврат из прерывания). Эта команда выталкивает из стека адрес прерванной команды и старое слово состояния, тем самым восстанавливая приоритет процессора.

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

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

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

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

Аналогичное прерываниям средство реализовано в ряде ОС - например, в UNIX - для чисто программных событий. Это средство называется сигналами и используется для обработки исключительных ситуаций и, в некоторых случаях, для межпроцессной коммуникации Сигналы в UNIX часто используются для принудительного завершения программы, и даже команда посылки сигнала называется kill - убить Легко видеть, что прерывания и сигналы могут служить для оповещения программы о событии, но не решают ни одной из проблем, перечисленных в разделе 4.2., а напротив, создают их. Действительно, подпрограмму обработки прерывания во многих отношениях можно рассматривать как параллельно исполняемый процесс, и к ней вполне приложимо все, что сказано в предыдущем разделе.

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

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

Кроме того, программу, работающую совместно с процедурами обработки прерываний, нельзя представить в виде детерминированного конечного автомата. Это усложняет анализ алгоритмов и доставило в свое время много волнений теоретикам программирования. Например, в [7] Дийкстра очень эмоционально описывает свою реакцию при первом столкновении с системой, использующей прерывания.

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

Но как программа узнает, что события еще не происходило?

Действительно, вернемся к примеру из предыдущего раздела: программа A производит данные, а программа B их потребляет. Наиболее простым решением было бы объединить эти программы в один процесс. Тогда программа A, произведя очередную порциюданных, вызывала бы программу B, и ждала бы, пока та пережует эту порцию. Или наоборот, программа B, обнаружив, что ей нужны очередные данные, вызывала бы программу A. Понятно, что в этом случае мы отказываемся от всех преимуществ многопроцессности.

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

  • Программа B, когда ей нужны очередные данные, проверяет флаг Если флаг равен 1, данные готовы. Если не равен, то нужно заснуть до получения сигнала.

  • Программа A, подготовив очередную порцию данных, посылает сигнал программе B.

  • Обработчик сигнала устанавливает флаг в 1 и возобновляет выполнение программы B.

3 типа прерываний:

  1. документированные;

  2. недокументированные;

  3. свободные.

  1. – перечислены во всех справочниках и т.д.; они гарантируют свою работу (т.е. то, что положено получить по справочнику, то мы и получим);

  2. – не дают гарантии; иногда работают хорошо, а иногда – плохо; т.е. зависят от состояния машины;

  3. – используются для нужд пользователя и не влияют на состояние работы ЭВМ (MS Windows “нахватал” их себе в достаточном количестве); т.е. поработали, затем отпустили.

Примечания:

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

а) это процедуры дальнего вызова, т.е. имя_процедуры far;

б) выталкиваются при помощи iret или retf, т.е. выталкиваются 6 байт вместо 4-ех (как для ближнего вызова, где используется уже ret);

в) для аппаратных прерываний есть несколько дополнений, суть которых заключается в перепрограммировании микросхемы PIC, отвечающей за прерывания, которая, в свою очередь, перепрограммируется через порт 20h, т.е.:

mov al, 20h ; только для аппаратных прерываний

out 20h, al ; 20h – чистой воды совпадение, не более того

! У таймера наивысший приоритет (0).

Схема использования прерывания 61h:

  1. з

    08h – прерывание таймера

    апоминание адреса 08h int 08h;

  2. запоминание адреса 61h  int 61h;

  3. int 08h  61h;

  4. 08h  связываем свой обработчик прерываний.

Переключение можно осуществлять, исходя из:

Счетчик mod n, где n – число процессов, т.е. если остаток от деления равен 0  12, если 1  23, если 2  31.