Глава 1. Системные ресурсы.
Раздел 3. Управление программами.
1.3.1 Манипуляции с памятью.
Kогда MS DOS загружает программу, то она помещается в младшую область памяти, сразу же за COMMAND.COM и установленными драйверами устройств или другими утилитами, которые резидентны в памяти. В этот момент времени вся память за программой отведена этой программе. Если программе нужна память для создания области данных, то она может приближенно вычислить где в памяти кончается ее код и затем поместить требуемую область данных в любое место за концом кода. Для определения адреса конца программы поместите в конце программы псевдосегмент типа:
ZSEG SEGMENT | |
; | |
ZSEG ENDS |
В ассемблере IBM PC ZSEG будет последним сегментом, так как сегменты располагаются в алфавитном порядке. С другими ассемблерами нужно действительно поместить эти строки в конце программы. В самой программе достаточно поставить оператор MOV AX,ZSEG и AX будет указывать на первый свободный сегмент памяти за программой.
Такой подход будет работать до тех пор, пока программа не будет предполагать о наличии памяти, которой на самом деле нет. Он не будет также работать в многопользовательской среде, когда несколько программ могут делить между собой одну и ту же область адресов. Для решения этой проблемы MS DOS имеет возможность отслеживать 640K системной памяти и отводить по требованию программы блоки памяти любого размера. Блок памяти - это просто непрерывная область памяти, его максимальный размер определяется размером доступной памяти, в частности, он может быть больше одного сегмента (64K). Если затребован слишком большой блок, то DOS выдает сообщение об ошибке. Любая возможность перекрытия блоков исключена. Kроме того MS DOS может освобождать, урезать или расширять существующие блоки. Хотя программа не обязана использовать эти средства, но удобно и предусмотрительно делать это. Hекоторые функции DOS требуют, чтобы были использованы средства управления памятью DOS, например, завершение резидентной программы {1.3.4} или вызов другой программы из данной {1.3.2}.
Прежде чем отвести память, существующий блок (вся память от начала программы до конца) должен быть обрезан до размера программы. Затем, при создании блока, DOS создает 16-байтный управляющий блок памяти, который расположен непосредственно перед блоком памяти. Первые 5 байтов этого блока имеют следующее значение:
байт 0 ASCII 90 - если последний блок в цепочке, иначе ASCII 77. байты 1-2 0 если блок освобожден байты 3-4 размер блока в 16-байтных параграфах
DOS обращается к блокам по цепочке. Адрес первого блока хранится во внутренней переменной. Значение этой переменной позволяет DOS определить положение первого отведенного блока, а из информации, содержащейся в нем, может быть найден следующий блок и т.д., как показано на рис. 1-4. Kак только Вы начали использовать систему распределения памяти DOS, то Вы обязаны придерживаться ее. Если программа изменит содержимое управляющего блока, то цепочка будет разорвана и DOS начнет выдавать сообщения об ошибке.
MS DOS обеспечивает три функции распределения памяти, номера от 48H до 4AH прерывания 21H. Функция 48H отводит блок памяти, а 49H - освобождает блок памяти. Третья функция ("SETBLOCK") меняет размер памяти, отведенной для программы; эта функция должна быть использована перед двумя остальными. После ее выполнения можно спокойно отводить и освобождать блоки памяти. Программа должна освободить все отведенные ею блоки перед завершением. Иначе эта память будет недоступной для последующего использования.
Средний уровень.
Все три функции распределения памяти прерывания 21H используют 16-битный адрес начала блока памяти, с которым они оперируют. Этот адрес соответствует сегменту, с которого начинается блок (блок всегда начинается со смещения 0 данного сегмента). Таким образом реальный адрес ячейки начала блока равен этому адресу, умноженному на 16. Также, для всех трех функций, BX содержит число 16-байтных разделов памяти (параграфов), которые будут отводиться или освобождаться. Если функция не может быть выполнена, то устанавливается флаг переноса, а в AX возвращается код ошибки, объясняющий причину. Возможны три кода ошибки:
7 разрушен управляющий блок памяти 8 недостаточно памяти для выполнения функции 9 неверный адрес блока памятиФункция отведения блока использует коды 7 и 8, а освобождения - 7 и 9, в то время как функция изменения блока использует все три кода. В следующем примере сначала отводится блок, размером 1024 байта. При этом BX содержит требуемое число 16-байтных параграфов, а при завершении стартовый адрес блока равен AX:0 (т.е. смещение 0 в сегменте со значением, содержащимся в AX). Вторая часть примера освобождает этот же блок, как и требуется при завершении программы. В данном случае значение полученное в AX помещается в ES. DOS следит за размером блока и знает какое количество параграфов надо освободить.
;---отведение блока размером 1024 байта | |
MOV AH,48H | ;номер функции |
MOV BX,64 | ;требуем 64 параграфа |
INT 21H | ;пытаемся отвести блок |
JC ERROR | ;обрабатываем ошибку в случае неудачи |
MOV BLOCK_SEG,AX | ;иначе сохраняем адрес блока |
. | |
;---освобождаем тот же блок | |
MOV AX,BLOCK_SEG | ;получаем стартовый адрес блока |
MOV ES,AX | ;помещаем его в ES |
MOV AH,49H | ;номер требуемой функции |
INT 21H | ;освобождаем блок памяти |
Hаконец, приведем пример использования функции 4AH. ES содержит значение сегмента PSP, т.е. самого первого байта памяти, с которого загружена программа. Это значение присваивается ES при старте задачи. Для использования SETBLOCK надо либо вызывать эту функцию в самом начале программы (прежде чем ES будет изменен), либо сохранить его начальное значение для последующего использования.
BX содержит требуемый размер блока в 16-байтных параграфах. Для определения этого размера поместите добавочный "искуственный" сегмент в конец программы. В макроасссемблере IBM PC сегменты располагаются в алфавитном порядке, поэтому Вы можете поместить его в любое место программы, при условии, что его имя это что-то вроде "ZSEG". В других ассемблерах действительно помещайте фиктивный сегмент в конец программы. Программа может прочитать позицию этого сегмента и, сравнивая ее со стартовым сегментом, получить количество памяти, требуемое самой программе. В момент загрузки программы и ES и DS содержат номер параграфа самого начала программы в префиксе программного сегмента; для COM файлов CS также указывает на эту позицию, но для EXE файлов это не так.
MOV BX,ZSEG | ;получаем # параграфа конца программы + 1 |
MOV AX,ES | ;получаем # параграфа начала программы |
SUB BX,AX | ;вычисляем размер программы в параграфах |
MOV AH,4AH | ;номер функции |
INT 21H | ;освобождаем память |
JC MEMORY_ERROR | ;проверяем на ошибку |
;--- | |
ZSEG SEGMENT | |
ZSEG ENDS |
<~-Раздел 3. Управление программами.
Содержание
1.3.2 Запуск одной программы из другой.-~>