Системное программное обеспечение персональных ЭВМ

         

Условия активизации и обработка прерываний


15.3. Условия активизации и обработка прерываний

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

Мы определили, что TSR-программа должна активизироваться по нажатию "горячей клавиши". Следовательно, первым прерыванием, которое должна обрабатывать программа является прерывание 9 - от клавиатуры. Обработчик прерывания 9 в нашей программе - функция new_9. По этому прерыванию программа должна читать скан-код нажатой клавиши и состояние клавиш-переключателей и сравнивать со своей "горячей комбинацией" (в нашей программе "горячей" является комбинация Alt+1, определяемая начальными значениями переменных hot_scan и hot_status). Если опознана "горячая комбинация" то код клавиши удаляется из буфера клавиатуры, в противном случае вызывается системный обработчик прерывания 9. Но может оказаться, что немедленную активизацию TSR-программы производить нельзя. Это обусловлено тем, что функции DOS нереентерабельны. Если мы прервали выполнение функции DOS и активизируем TSR-программу, которая, в свою очередь, будет обра-щаться к функциям DOS, то это может привести к катастрофическим последствиям. Поэтому лучше всего не активизировать TSR-программу сразу же после распознавания "горячей комбинации", а отложить активизацию до тех пор, пока не будут выполнены условия, делающие активизацию возможной, а в обработчике прерывания от клавиатуры только установить признак (hot_key) того, что "горячая клавиша" была нажата. Флаг tsr_run, также анализируемый в обработчике этого прерывания, блокирует реентерабельный вызов TSR-программы: его значение 1 сигнализирует о том, что TSR-программа уже активизирована.

Проверку этих условий удобно производить по таймеру. Следовательно, второе прерывание, которое должна обрабатывать наша программа - 8 (или 0x1C) - от таймера. Обработчик прерывания 8 в нашей программе - функция new_8. При каждом прерывании от таймера проверяется прежде всего флаг hot_key.
Если он взведен, т.е. " горячая клавиша" была нажата, то проверяется флажок занятости DOS, который устанавливается DOS в 1 при выполнении любой функции DOS (адрес флажка занятости мы определили при инициализации программы). Еще одно условие инициализации - нулевое значение флага disk_op, о котором мы расскажем ниже. Если выполнены все условия инициализации, то из функции 8 вызывается функция act_tsr, а флаг "горячей клавиши" сбрасывается.

Но полностью отказываясь от активизации TSR-программы в моменты выполнения любых функций DOS, мы можем отложить эту активизацию на весьма неопределенный срок. Например, при отсутствии выполняемых программ командный процессор находится в состоянии ожидания ввода с клавиатуры, при этом выполняется функция DOS 0x0A, следовательно, флажок занятости взведен. Имеется, однако, способ избежать такой блокировки активизации. Основным фактором нереентерабельности функций DOS является то, что разные функции DOS используют для своей работы один и тот же стек, поэтому вложенный вызов функции DOS портит стек вызывающей функции. Оказывается однако, что в DOS имеется два стека - один используется функциями с номерами до 0x0C включительно, а второй - функциями с большими номерами. Если DOS выполняет функцию с номером не выше 0x0C, то TSR-программа может активизироваться, но при своем выполнении она не должна обращаться к функциям с меньшими номерами (что, впрочем, не составляет больших проблем). Индикатором такого состояния DOS является прерывание 0x28, выдаваемое DOS, когда DOS находится в состоянии ожидания ввода. Следовательно, еще одно обрабатываемое нами прерывание - 0x28 (обработчик - new_28). Обработчик этого прерывания вызывает функцию act_tsr при установленном флаге "горячей клавиши" даже при установленном флажке занятости DOS.

Еще одно прерывание, которое необходимо перехватывать нашей программе - 0x13 (обработчик new_13) - прерывание BIOS, обеспечивающее дисковые операции. Дисковые операции не следует прерывать хотя бы потому, что некоторые из них требуют временной синхронизации.


Поэтому основная задача нашего обработчика new_13 - установить флаг disk_op, который блокирует активизацию TSR-программы на время выполнения дисковых операций. Для выполнения операции наш обработчик обращается к прежнему (системному) обработчику этого прерывания и обеспечивает возврат тех значений регистров AX, CX, DX и флагов, которые устанавливает системный обработчик (для формирования флагов приходится обращаться к функции new_2F).

И еще одно прерывание, которое обрабатывает наша программа - 0x2F, будет рассмотрено при обсуждении программных коммуникаций.

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

Для получения и установки векторов прерываний служат у нас функции get_vector и set_vector, использующие функции DOS 0x35 и 0x25 соответственно. Представляет, однако, интерес организация передачи управления нашим обработчикам прерываний (функция set_vectors). Выделяется отдельный блок оперативной памяти размером 2 параграфа, в котором размещаются 5 команд "дальнего" перехода на наши функции обработки пяти перехватываемых при инициализации прерываний. В программе команда перехода описывается структурой struct far_jmp, в поле jmp которой заносится код команды JMP - 0xEA, а в поле int_h - адрес функции, на которую производится передача управления. В таблицу векторов записываются адреса сформированных команд перехода. Адреса прежних обработчиков прерываний запоминаются в массиве old_v. Для прерывания 0x2F сегментная часть и смещение адреса прежнего обработчика также запоминаются в дополнительной переменной a_2F. Почему же мы сразу не записали в таблицу векторов адреса наших обработчиков? Это сделано для предупреждения возможной аварии при выгрузке нашей программы (эта проблема была поставлена еще в главе 3). Наша программа включила свои обработчики прерываний в цепочки.


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

Восстановление векторов выполняется функцией restore_vectors. Эта функция читает из таблицы векторов вектора всех перехватываемых нашей программой прерываний и сравнивает их с адресами наших обработчиков. Если для какого-либо прерывания вектор содержит адрес нашего обработчика, то это значит, что наш обработчик является последним в цепочке обработки этого прерывания, в этом случае мы можем спокойно восстанавливать сохраненный при инициализации прежний вектор в таблице. Если же вектор содержит не наш адрес, это означает, что после нас к цепочке подключилась другая программа (возможно даже не одна), в этом случае мы не меняем таблицу векторов, но в команде перехода меняем адрес перехода на адрес прежнего обработчика. Если хоть один вектор прерывания не удалось восстановить в таблице векторов, то блок памяти, содержащий команды перехода, должен быть оставлен в памяти после удаления из нее нашей программы. Для этого в поле "владельца" 'этого блока, которое до сих пор содержало PID нашей программы, мы записываем код 8 - признак принадлежности этого блока DOS, что защитит его от удаления.

Содержание раздела