|
Техника
программирования на Turbo C
Глава 11
Автор: Ал. Стивенс
Прерывание от клавиатуры
(9).
При нажатии пользователем "горячего
ключа" резидентная программа должна прервать все, что бы ни делала
система, и запустить себя. Чтобы сделать это, программа должна сканировать
клавиатуру в поисках "горячего ключа". Понимание этого процесса
требует понимания, как работает прерывание от клавиатуры и сканирование
кодов.
Нажатие клавиши генерирует прерывание
9, и обработка передается программе по адресу вектора прерывания 9.
Она должна прочитать порт данных клавиатуры и обработать это значение.
Клавиатура PC генерирует код сканирования, который может быть прочитан
из входного порта клавиатуры, а не значение ASCII, поступающее в вашу
программу при использовании функции get_char. Каждая клавиша на клавиатуре
имеет свое собственное значение. Программа должна определить по нему,
какая клавиша или их комбинация нажата, и что делать с ними.
Программа обработки прерывания от клавиатуры,
находящаяся в ROM-BIOS (базовой системе ввода-вывода в ПЗУ), читает
коды сканирования, переводит их в коды ASCII, и пересылает их в буфер
клавиатуры. Программы (включая ДОС), читающие с клавиатуры, читают коды
из буфера. ROM-BIOS поддерживает байт состояния, показывающий, были
ли нажаты клавиши Alt, правый Shift, левый Shift, и Ctrl. Программа
может прочитать эти значения, восстановив у себя байт состояния. В главе
12 иллюстрируются коды сканирования и значения байта состояния клавиатуры.
TSR-программа может присоединить себя
к прерыванию 9. Подпрограмма обработки прерывания в резидентной программе
может поддерживать выполнение программы из ROM-BIOS, читая коды сканирования
и байт состояния, и проверяя, не нажат ли "горячий ключ".
Если нет, то обработка дальше предоставляется программе из ROM-BIOS.
Если в TSR-программе нет вызовов функций
ДОС, подпрограмма обработки прерывания 9 может непосредственно выполнить
свою функцию. Если же необходимо использовать функции ДОС, подпрограмма
должна выставить флаг присутствия "горячего ключа" и закончить
работу. Другие подпрограммы обработки прерывания должны заметить этот
флаг и выполниться, если это безопасно. Одна из подпрограмм, ждущая
подходящего момента для выполнения функции, - это подпрограмма обработки
прерывания от таймера.
Прерывание от таймера.
Работа PC прерывается 18.2 раза в секунду
системным таймером, который использует вектор прерывания 0х1c для запуска
подпрограммы обработки. В главу 12 включен пример
резидентной программы "часы", использующей прерывание от таймера.
TSR-программы, вызываемые по "горячему ключу", также должны
использовать это прерывание, поэтому надо прикрепиться и к вектору 0х1с.
Так как программа не может выполняться, пока не убедится, что использование
функций ДОС безопасно, то надо проверить, установлен ли флаг "горячего
ключа", как описано выше, и затем проверить безопасность. Если
эти два условия выполнены, то подпрограмма обработки прерывания от таймера
может запускать основную резидентную программу.
Как и в случае с прерыванием от клавиатуры,
должна быть реализована возможность работы с прерыванием от таймера
программе, которая до этого была обработчиком такого прерывания.
Проблема реентерабельности
ДОС.
Вы уже читали о проблемах, возникающих
при использовании функций ДОС из резидентных программ.В случае, когда
программа прерывает ДОС и хочет использовать ее функции, ДОС не является
реентерабельной. Может случиться так, что при нажатии "горячего
ключа" ДОС будет находиться в состоянии ввода с клавиатуры, и пользователю
не удастся быстро вызвать TSR-программу, так как до окончания ввода
она не может начать выполнение.
Два стека ДОС.
ДОС поддерживает два стека.Когда ДОС
выполняет одну из функций с номером от 0 до 12, используется первый
стек; при выполнении других функций используется второй. ДОС сохраняет
значение сегментного регистра стека и регистра смещения стека для каждой
группы функций отдельно; но реентерабельных функций, однако, нет. Сохраненные
значение регистров при первом вызове функции могут быть затерты при
следующем вызове.
ДОС лучше определить как полуреентерабельную.
При прерывании выполнения функций 0-12 безопасно вызывать остальные
функции, и наоборот.
Можно легко избежать использования функций
0-12. Они предназначены для организации консольного ввода и вывода.
Но есть много других способов, чтобы управлять клавиатурой и экраном,
и частенько они работают лучше, чем функции ДОС. Использования остальных
функций не так-то легко избежать. С их помощью можно делать многое из
того, что нужно в резидентной программе, включая управление файлами.
Если вы можете избежать использование
функций 0-12, но нуждаетель в остальных функциях, то ситуация с двумя
стеками работает на вас. При уверенности, что прерывания по вызову вашей
программы будут происходить не во время работы функций второй группы,
проблемы реентерабельности не существует. Задачу можно решить установкой
флажка ввода "горячего ключа" и ожиданием окончания работы
небезопасных функций ДОС, если таковая имеет место. Эти функции работают
быстро, так что большой задержки не получится.
Теперь остальсь узнать, каким способом
получить информацию о возможности использовать ДОС. Такую информацию
содержит системный флажок занятости.
Системный флажок занятости
(0х34).
Функция ДОС 0х34 возвращает сегментный
адрес и смещение специального флага в памяти, поддерживаемого ДОС. Он
называется системным флажком занятости. Этот флаг устанавливается, когда
ДОС выполняет одну из своих "небезопасных" функций. После
выхода из функции ДОС очищает флаг.
При первом запуске TSR-программы она
использует функцию ДОС 0х34 для нахождения и запоминания адреса системного
флажка занятости. При каждом прерывании от системного таймера обработчик,
находящийся в TSR-программе, проверяет наличие флажка "горячего
ключа", устанавливаемого обработчиком прерывания от клавиатуры,
и отсутствие системного флажка занятости. При наличии этих двух условий
обработчик таймера сбрасывает флажок "горячего ключа" и запускает
основную часть TSR-программы.
Такая процедура временами срабатывает.
Но временами ДОС остается занятой, пока пользователь занят чем-то другим,
например, набирает символы в командной строке. Если при этом он нажмет
"горячий ключ", то обработчик клавиатуры установит флаг "горячего
ключа", но обработчик таймера никогда не запустит TSR-программу.
Чтобы бороться в таких условиях, надо использовать вектор прерывания
DOSOK.
Прерывание DOSOK.
Часто наступают периоды, когда ДОС занята,
но можно использовать вторую группу функций; например, когда ДОС ожидает
ввода строки символов. В этом случае ДОС вызывает прерывание 0х28 -
DOSOK. Цель этого - известить TSR-программу (в частности - системный
спулер PRINT.COM ), о том, что можно использовать функции второй группы.
Если никакая программа не прикреплена к этому прерыванию, оно просто
указывает на команду IRET. Прерывание 0х28 - программное прерывание;
обработчик, прикрепленный к нему, будет вызываться из другой программы
по команде INT 0х28.
TSR-программы могут прикрепляться к прерыванию
DOSOK и использовать его для обнаружения занятости ДОС. Обработчик этого
прерывания из TSR-программы проверяет установку флажка "горячего
ключа" и в зависимости от его значения либо запускает основную
часть TSR-программы; либо возвращает управление программе, вызвавшей
это прерывание, или программе, ранее прикрепленной к этому прерыванию.
Прерывание DOSOK вызывается в случае,
когда ДОС занята; однако TSR-программа, вызванная в это время, не может
быть прервана другой такой же программой до тех пор, пока выполняющаяся
TSR-программа не сделает вызов прерывания 0х28. Причина этого ограничения
в том, что когда ДОС вызывает прерывание DOSOK, флажок занятости ДОС
остается установленным. При выполнении TSR-программы он не меняет значения,
и, если пользователь нажмет "горячий ключ" для другой TSR-программы,
ее обработчик таймера будет ожидать, пока флажок занятости не сбросится.
А это не случится до окончания текущей TSR-программы. Такая ситуация
всегда случается при вызове TSR-программы в момент, когда ДОС ожидает
ввода команды пользователя.
Резидентная программа, чтобы разрешить
прервать свое выполнение другой такой же программе, должна вызвать прерывание
DOSOK в подходящее время. Лучше всего это сделать, когда текущая программа
ожидает ввода строки от пользователя. Если вы обратили внимание, функция
get_char из главы 4 вызывает прерывание 0х28 во время ожидания ввода
строки. Это делается для поддержки концепции резидентных программ, описанной
в этой главе и используемой в главе 12.
Дисковое прерывание ROM-BIOS.(0х13)
Дисковые операции нельзя прерывать из-за
возникновения ошибок в прерванной программе. Чтобы избежать этого, TSR-программа
должна присоединить себя к дисковому прерыванию ROM -BIOS 0х13. Если
какой-либо процесс вызывает это прерывание, TSR- программа устанавливает
флаг и затем передает управление по старому адресу прерывания. Когда
обработка прерывания 0х13 завершается, TSR-программа очищает флаг. Этот
флаг проверяется ISR таймера и DOSOK. Если производится дисковая операция,
то TSR не разрешает прерывание.
Прерывание тяжелой ошибки в ДОС.(0х24)
При возникновении тяжелой ошибки ДОС
вызывает прерывание 0х24. Например, при обращении к дискете в открытом
дисководе, ДОС обнаруживает состояние неготовности и вызывает прерывание
тяжелой ошибки. Если программа не присоедиила себя к вектору 0х24, все
критические ошибки будут обрабатываться ISR в командном процессоре ДОС.
Он выдает сообщение "Abort,Retry, or Ignore" на экран. ISR
возвращает в ДОС значение, определяющее порядок дальнейшей обработки.
Предположим, что ни одна программа не
присоединена к вектору 0х24. Ваша TSR прерывает нерезидентную программу,
и пытается прочитать дискету из открытого дисковода. Командный процессор
ДОС перехватывает управленеие и запрашивает пользователя о дальнейших
действиях. Пользователь отвечает "A" (Abort-прервать), и ДОС
пытается прервать выполнение программы. Если ваша TSR переключила адрес
PSP, то ДОС будет пытаться прервать TSR. ДОС не знает, что в памяти
находится нерезидентная программа в прерванном состоянии, и не возвращает
ей управление. Система ломается. Если TSR не переключала PSP, то ДОС
завершает нерезидентную программу и не возвращает управление TSR.
Предположим, что нерезидентная программа
присоединилась к вектору 0х24. Дисковая ошибка в вашей программе вызовет
исполнение обработчика тяжелой ошибки из прерванной нерезидентной программы.
TSR должна присоединяться к вектору 0х24
в любом случае, будет ли или не будет в ней какая-либо обработка ошибок.
Вектор присоединяется при "всплытии" TSR и восстанавливается
на тот, что был, при возвращении управления в прерванную программу.
Никакие обстоятельства не должны заставить TSR просить ДОС прервать
обработку. Большинство TSR-программ просто игнорируют ошибки и говорят
ДОС также их игнорировать. Прерывание тяжелой ошибки не связывается
в цепочки - это опасно. Если другая TSR-программа (например, спулер)
присоединила к себе вектор 0х24 и вызвана после вашей TSR, она получит
ваши ошибки. Если же эта программа была вызвана до вашей TSR, то вы
будете получать ее ошибки и говорить ДОС игнорировать их, что является
проблемой, которую нельзя разрешить без написания системно-ориентированного
обработчика ошибок.
Прерывание Ctrl-Break в
ДОС.(0х23)
При нажатии пользователем клавиш Ctrl-Break
ДОС отображает на экране в текущей позиции курсора символы ^C и вызывает
прерывание 0х23. Обработчик этого прерывания, имеющийся в ДОС, вызывает
немедленное завершение текущей программы. TSR-программу завершать таким
способом нельзя: слишком много векторов прерываний прикреплено к ней,
и нерезидентная программа может находиться в памяти за ней. Если вы
просто проигнорируете это прерывание, имеется риск, что другая программа,
загруженная за вашей, присоединит себя к этому прерыванию и сделает
что-то неподходящее при нажатии клавиш Ctrl-Break во время выполнения
вашей программы.
В ДОС имеется функция (0х33), которая
позволяет программе читать текущий статус (разрешено/запрещено) обработки
Ctrl-Break и устанавливать его. При вызове TSR-программы она должна
прочитать текущее значение статуса, и затем запретить обработку Ctrl-Break.
При завершении своей работы TSR должна восстановить значение статуса
обработки Ctrl-Break. Присоединяться к этому прерыванию нет необходимости.
После таких действий вашей TSR в случае
нажатии Ctrl-Break во время ее выполнения, после возврата в прерванную
программу она будет закончена ДОС. Если все TSR будут использовать такой
способ, то завершаться по Ctrl-Break будут только нерезидентные программы.
Выполнение TSR-программы.
Выполнение TSR-программы проходит в два
этапа. Первый этап - когда пользователь запускает ее из командной строки.
Программа выполняет инициализационный код, сохраняет свой контекст,
присоединяет себя к прерываниям, если это нужно, и завершается с использованием
функции ДОС TSR, таким образом объявляя себя резидентной.
Второй этап - когда TSR-программа запускается
в результате одного из прерываний, к которому она прикрепилась. В большинстве
случаев, векторы прерываний связываются в цепочки, как это было описано
ранее. Программа определяет, может ли она выполняться - это зависит
от состояния некоторых важных системных индикаторов, описанных выше.
Если она может выполняться, она сохраняет контекст прерванной программы,
восстанавливает свой собственный контекст, выполняет свою задачу, восстанавливает
контекст прерванной программы, и передает ей управление.
Завершение TSR-программы.
Могут быть причины, по которым вы можете
захотеть завершить TSR-программу, что отнюдь не является легкой задачей.
Вспомните, ведь ДОС не знает ничего о программе, оставшейся резидентной,
и вам самим надо сделать все то, что обычно делает ДОС по отношению
к обычным нерезидентным программам.
Завершение TSR-программы включает в себя
следующие шаги:
1. Сообщение программе о том, что ей
надо завершиться.
Для этого можно использовать другой "горячий
ключ", или коммуникационный вектор прерывания, указывающий на сигнатуру
в программе, означающую, что программа уже в памяти. Второй метод используется
в драйвере TSR-программ, описанном в главе 12.
При этом пользователь, знающий, что программа уже резидентна, запускает
ее второй раз, давая в командной строке параметр, означающий, что программа
должна завершиться. Программа ищет свою сигнатуру способом, описанным
выше. При нахождении сигнатуры она также находит свой вектор прерывания.
Через этот вектор она посылает команду завершения свой резидентной копии,
после чего та совершает следующие шаги завершения.
2. Восстановление векторов прерывания
в первоначальное состояние.
У вас может быть возможность сделать
это, а может и не быть. Если другая TSR-программа была загружена после
вашей, и она связала векторы в цепочки, то вы успешно дезактивируете
ее при восстановлении векторов. Далее, если эта TSR-программа будет
затем завершена, то она переустановит вектора на адреса в вашей программе,
по которым уже может находиться неизвестно что. Так что до того, как
восстанавливать вектора, надо убедиться, что ваша программа до сих пор
владеет ими. Чтобы определить это, надо сравнить адреса, находящиеся
в соответствующих векторах, с адресами соответствующих обработчиков
в вашей программе. Если какой-либо вектор изменен, то ваша программа
уже не владеет им, поэтому завершать ее нельза и надо просто приостановить
ее.
Еще одним осложнением, возникающем при
завершении вашей TSR с другой программой такого же типа, загруженной
после нее, является фрагментация памяти в нерезидентной области. ДОС
будет использовать область, освобожденную вашей программой, при запросах
на память из выполняющихся программ, но для загрузки других программ
эта область не будет использоваться. Помните, что ДОС - однозадачная
ОС - не понимает концепции фрагментированных программ, потому что она
не понимает мультипрограммирование.
3.Закрытие всех файлов, открытых в программе.
При завершении нерезидентной программы
ДОС автоматически закрывает все файлы, сканируя массив указателей файлов
программы в PSP и закрывая все вхождения в него, которые еще используются.
Помните, что эти вхождения - это номера элементов массива, который поддерживает
ДОС. При завершении и объявлении резидентной ДОС не закрывает файлы,
открытые этой программой, и при завершении работы TSR-программы необходимо
это сделать. Если файлы не будут закрыты, то элементы массива не будут
освобождены, и будут недоступны для дальнейшего использования другими
программами. Загрузка и завершение без закрытия файлов TSR-программ
может привести к исчерпанию таблицы файлов ДОС и "зависанию"
системы.
Элементы в таблице указателей файлов
представляют файлы на уровне указателей. Это эквивалентно функциям небуферизованного
ввода-вывода низкого уровня в Си. Функции Си, выполняющие буферизованный
потоковый ввод-вывод, поддерживают собственные буферы и указатели и
могут потребовать особого порядка закрытия, называемого потоковым. Такое
закрытие - это библиотечная функция Си, отличающаяся от соответствующей
функции ДОС. Такие файлы надо закрывать стандартной функцией fclose.
Файлы, открытые функциями open и creat, могут быть закрыты функцией
close.
4.Возврат памяти, занимаемой программой,
в ДОС.
Для этого можно использовать функцию
ДОС 0х49. TSR-программой, как минимум, занимается два блока памяти.
Первый - это блок системных параметров, адрес которого хранится в PSP
со смещением 0х2с. Второй блок - это сам PSP. Если программе выделены
оба этих блока, то они оба должны быть возвращены ДОС. В главе 12 демонстрируется
техника сканирования списка управляющих блоков памяти ДОС для нахождения
и возвращения в ДОС блоков памяти, выделенных программе.
Приостановка и возобновление
выполнения TSR-программы.
При приостановке TSR-программы ее не
удаляют из памяти, а только дают ей команду не реагировать на прерывания
путем установки флага. Команда возобновления сбрасывает этот флаг. С
этой целью можно использовать коммуникационный вектор прерывания.
TSR-драйвер в главе 12 использует эту
технику для завершения, приостановки, и возобновления выполнения TSR-программ.
Выводы.
В этой главе описывается операционное
окружение резидентных программ. В главе 12 на
примере двух таких программ описывается весь процесс шаг за шагом. Первая
программа - это оперативная программа-часы, которая поддерживает на
экране постоянное отображения текущих времени и даты. Вторая программа
более интересна - это TSR-драйвер. Вы связываете свою программу на Турбо
Си c этим драйвером, используя некоторые соглашения для инициализации,
и ваша программа на Турбо Си становится резидентной. Это значит, что
программа будет вызываться по нажатию клавиши, что она может открывать,
читать, пиать в и закрывать дисковые файлы, что она использует ROM-BIOS
функции для чтения с клавиатуры и прямой доступ к видеопамяти для вывода
на экран, и что она никогда не выходит в ДОС.
|
|