Читайте также:
|
|
Отложим на время описание способа, с помощью которого резидентная программа может выгрузить себя из памяти, и займемся более детально процессом инициализации.
На первом этапе инициализации наша резидентная программа резервирует память для области кучи (heap) и стека. Это необходимо, если функция, получающая управление при активизации программы, работает со стандартными функциями из библиотеки транслятора (например, открывает файлы или потоки ввода/вывода, заказывает области памяти функцией malloc и т. п.).
Для того чтобы вам была понятна процедура резервирования памяти, а также способ, при помощи которого мы вычисляем размер области памяти, передаваемой в качестве параметра функции _dos_keep, рассмотрим расположение различных сегментов, составляющих программу в модели памяти small. Эта модель используется в нашей программе TSRDEMO. Описание расположения сегментов для других моделей памяти вы сможете найти в руководстве программиста, которое поставляется вместе с Borland C++ (или другим аналогичным средством разработки).
В модели памяти small программе выделяются две области памяти размером не более 64 Кбайт. Одна область содержит сегмент кода с названием _TEXT и содержит исполнимый код программы, в другой расположены сегмент инициализированных данных с названием _DATA, сегмент неинициализированных данных _BSS, область кучи и сегмент стека. Перед сегментом кода расположен сегмент PSP (рис. 5.1).
Рис. Расположение сегментов в модели памяти small
Из этого рисунка видно, что вслед за сегментом PSP в памяти располагается сегмент кода, а затем - сегмент данных.
Какие из этих сегментов потребуются программе, после того как она останется резидентной в памяти?
Блок памяти, выделенный в сегменте PSP для хранения переменных среды, очевидно, не нужен, так как его содержимое может быть прочитано на этапе инициализации и в последующем останется неизменным. С сегментом кода тоже все ясно - его нужно оставить в памяти.
Если резидентная программа составлена на языке программирования С, то наибольшие трудности возникают при определении необходимого размера сегмента данных.
Программа может иметь статические глобальные переменные, имеющие или не имеющие начальные значения. Они располагаются, соответственно, в сегментах _DATA и _BSS, доступ к которым возможен через сегментный регистр DS. Размер указанных сегментов можно определить только приблизительно, зная общий объем памяти, выделенной программой для статических и глобальных переменных.
Вслед за сегментом _BSS в памяти расположена область HEAP, которая иногда называется просто кучей. Из этой области (доступ к которой возможен с помощью регистра DS) происходит динамическое выделение памяти по явному или неявному запросу программы. Заметим, что вызов многих функций стандартной библиотеки С приводит к неявному выделению памяти из области HEAP.
Механизм выделения памяти из области HEAP имеет одну особенность: если программа заказала для себя блок памяти из этой области (например, функцией malloc), а затем освободила его, этот блок памяти не будет возвращен MS-DOS до завершения работы программы. Когда программе опять потребуется блок памяти, по возможности он будет выделен из области HEAP без обращения к соответствующей функции прерывания MS-DOS.
Что же касается стека, то, как известно, он адресуется с помощью регистров SS:SP. Содержимое регистра SS после запуска программы в модели памяти small равно содержимому регистра DS, так как для этой модели памяти стек расположен в сегменте данных.
В отличие от области данных HEAP, которая растет в направлении к концу памяти (в сторону больших адресов), стек растет в направлении к началу памяти. На рис. 5.1 это показано при помощи стрелок.
Вслед за областью стека в памяти расположена область FAR HEAP. Блоки памяти из этой области выделяются в тех случаях, когда программа заказывает их такой функцией, как farmalloc.
Для того чтобы при активизации резидентная программа могла вызывать стандартные функции библиотеки транслятора С, необходимо зарезервировать достаточное количество памяти для областей HEAP и стека. Кроме того, при активизации необходимо правильно загрузить регистры SS:SP, чтобы они указывали на нижнюю границу блока памяти, выделенного резидентной программой для стека.
Как мы уже говорили, программа, составленная на языке программирования С, остается резидентной в памяти с помощью функции _dos_keep. Через первый параметр этой функции следует передать код завершения (для анализа в пакетных файлах), а через второй - объем памяти, который должен быть зарезервирован для программы после ее завершения. Этот объем нужно указать в параграфах.
Каждое аппаратное прерывание имеет свой номер (например, упомянутое выше прерывание имеет номер 5) и в оперативной памяти постоянно хранится таблица так называемых векторов прерываний, где вектор прерывания - это поле из четырёх байтов, в котором хранится адрес специальной программы - обработчика этого прерывания, которому передается управление всякий раз при наступлении соответствующего прерывания.
Работа программы построена на том, что в вектора прерываний 9 (нажата/отжата клавиша) и 1Ch (от таймера) на время работы резидентной программы засылаются адреса новых, созданных в данной программе обработчиков соответствующих прерываний.
В программе для запоминания адресов стандартных (старых) обработчиков прерываний 9 и 1Ch на время ее работы используются специальные переменные, это соответственно old_int09 и old_int1c.
Для такоих действий служат функции setvect и getvect, они имеют прототипы в файле dos.h
void setvect(int interruptno, void interrupt (*isr) ())
void interrupt (* getvect(int interruptno)) ()
Первая из них служит для того, чтобы в вектор прерывания с номером interruptno занести адрес isr нового обработчика этого прерывания, а вторая - возвращает адрес стандартного обработчика прерывания с номером interruptno.
На время этих действий с помощью специальной функции disable() запрещаются прерывания, а потом они вновь разрешаются с помощью функции enable(). Прототипы функций disable(), enable() содержатся в файле dos.h и имеют вид void disable(void) и void enable(void)
Новые обработчики соответствующих прерываний выполнены здесь в виде функций new_int09() и new_int1c().
В начале нашей программы в виде констант TAIL_PTR, HEAD_PTR, SOST_KLAV заданы адреса в буфере клавиатуры, по которым хранится смещение "хвоста" и "головы", а также задано смещение байта состояния переключающих клавиш. (Все смещения по отношению к адресу 0x40) В программе также используется целочисленная переменная active и сразу после загрузки резидентной программы она устанавливается в 0. Неравенство её нулю означает, что в данный момент идёт запись в буфер клавиатуры соответствующих символов слова. Обработчик new_int09() включается при нажатии и отпускании любой клавиши, сразу же он по адресу 0x40:SOST_KLAV считывает байт состояния переключающих клавиш и анализирует по нему прижата ли клавиша ScrollLock (ей отвечает единица в 4-м справа бите).Также анализирует пуст ли буфер клавиатуры (условия пустоты буфера клавиатуры - равенство смещений "головы" и "хвоста") и анализирует по переменной active чтобы не шло в данный момент занесение слова в буфер клавиатуры, т.е. чтобы active=0.
При выполнении этих трех условий он заносит 1 в active и присваивает -1 значению индекса i. Во всех случаях этот обработчик передаст управление соответствующему стандартному обработчику (прерывания 9).
Дальше включается в дело обработчик new_int1c() он, собственно, включается в дело постоянно с интервалом в доли секунды, но, когда active=0, он ничего не делает, а теперь,когда стало active<>0, он будет заносить в буфер клавиатуры с помощью уже знакомой нам функции 5 прерывания 16h из массива key_codes[] символы слова "printf", ведя им счет по индексу i и, занеся их все туда, сам установит active в 0.
И этот обработчик также в конце передаст управление соответствующему стандартному обработчику (прерывания 1Ch).
Дата добавления: 2015-10-16; просмотров: 64 | Нарушение авторских прав
<== предыдущая страница | | | следующая страница ==> |
Создать резидентную программу, которая выводит на терминал слово "else" по нажатию Alt. | | | ББК 36.99 |