TURBO PASCAL

Новости

Программы   

Turbo Pascal 

Игры

Документация   

Странности

FAQ

Ссылки

Форум

Гостевая книга

Рассылка

Благодарности

Об авторе

 

 

9.3. РАБОТА С ПАМЯТЬЮ

Для создания программы, работающей в защищенном режиме, выберите в среде ВР опцию Compile/Target и в появившемся диалоговом окне укажите Protected—mode. Получающийся в результате компиляции ЕХЕ— файл имеет два заголовка: стандартный заголовок MS-DOS и заголовок Windows. При запуске программы под управлением MS-DOS командный процессор COMMAND.COM использует заголовок ДОС для запуска небольшой стартовой программы, которая автоматически пристыковывается компилятором к исполняемому файлу. Эта программа осуществляет следующие действия:

• проверяет наличие DPMI— сервера и, если сервер не инсталирован, загружает его из файла DPMI16BI.OVL; если сервер инсталирован (обычно это означает, что программа запускается из расширенного режима Windows], стартовая программа не загружает его вновь;

• проверяет наличие в памяти расширителя ДОС и при необходимости загружает его из файла RTM.EXE (если программа запускается из другой программы, работающей в защищенном режиме, обе программы будут использовать один и тот же расширитель RTM};

• переключает процессор в защищенный режим и передает управление загрузчику исполняемых файлов расширителя ДОС.

Загрузчик расширителя читает Windows — заголовок исполняемого файла, загружает все указанные в нем DLL — библиотеки, затем загружает саму программу и передает ей управление.

Таким образом, для нормальной загрузки программы необходимо, чтобы в текущем каталоге или в одном из каталогов, перечисленных в переменной окружения PATH, находились все используемые программой DLL— библиотеки, а также файлы DPMI16BLOVL и RTM.EXE.

Программирование в защищенном режиме почти не отличается от программирования для MS-DOS. Единственное, о чем не должен забывать программист, это о специфике использования памяти.

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

Seg0040 определяет начало области параметров BIOS;

SegAOOO начало видеопамяти адаптеров EGA/VGA в графическом режиме;

SegBSOO начало видеопамяти адаптеров CGA/EGA/VGA в текстовом режиме;

SegBOOO начало видеопамяти монохромных адаптеров.

Только с правильными селекторами можно использовать массивы памяти Mem, MemW и MemL. Селектором должен быть и первый параметр обращения к функции Ptr, которая используется для создания указателей.

В защищенном режиме размер кучи определяется объемом всей оператчвной памяти компьютера за вычетом загруженной программы и резидентных частей MS-DOS, расширителя ДОС и DPMI— сервера. Хотя функции ClobaIAIIoc и GlobalAllocPtr способны распределять в куче блоки очень большие блоки (до 64 Мбайт), использовать данные в крупных блоках можно только порциями по 64 Кбайт, т.к. смещение в указателях задается 16—разрядным словом. Учитывая это, расширитель ДОС при распределении в куче крупного блока создает несколько последовательно расположенных селекторов, каждый из которых (кроме, возможно, последнего) ссылается на участок памяти длиной 64 Кбайт. Возвращаемый функциями GlobaILock и GlobalAllocPtr указатель содержит селектор и смещение, определяющие адрес самого первого байта крупного блока. Для доступа к остальным байтам в этом случае можно использовать следующую функцию:

Function GetPtr(P: Pointer; Offset: LongInt): Pointer/begin

GetPtr := Ptr(

HiWord(LongInt(P)) + HiWord(Off set)*SelectorInc,

LoWord(LongInt(P)) + LoWord(Offset)) end;

При рализации этой функции используются два обстоятельства. Во-первых, старшее слово 32 — разрядного параметра Offset содержит количество блоков по 65535 байт и поэтому для получения селекторной части указателя это слово умножается на Selectorinc (вариант

round(Offset/65535)*SelectorInc

дает такой же результат, но реализуется значительно медленно). Во-вторых, предполагается, что сложение

LoWord(LongInt(P)) + LoWord(Offset))

не приведет к переполнению значения Word. Это будет только тогда, когда смещение указателя Р нулевое. Если это не так, то в общем случае потребуется дополнительная проверка

if LongInt(LoWord(LongInt(P)))+LoWord(Offset)>$FFFF then {Наращиваем селекторную часть на Selectorinc}

К счастью, смещение в адресах, возвращаемых функциями GlobaILock и GlobalAllocPtr, всегда нулевое и функция не осуществляет ненужную проверку.

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

{$N+,E+} {Используем сопроцессор для типа Сотр} Uses WinAPI;

Function GetPtr(P: Pointer; Offset: Longint'): Pointer;

begin

GetPtr := Ptr(

HiWord(LengIntCP)) + HiWord(Offset)*SelectorInc, LoWord(Longint(P)) + LoWord(Offset)) end;

type

PLongInt =ALongInt;

const

MaxSize = 250000;

var

Summ : Comp;

k : Longint;

Buffer: Pointer;

begin

Buffer := GlobalAllocPtr(gmem_Moveable,MaxSize*4^;

for k := 1 to MaxSize do

PLongInt (GetPtr (Buf fer, (k-DM))'" := k;

Summ := 0;

for k := 1 to MaxSize do

Summ := Summ + PLongInt(GetPtr(Buffer,(k-1)*4))л;

GlobalFreePtr(Buffer) ;

WriteLn(Summ:10:0, (1.O*MaxSize*MaxSize+MaxSize)/2:15:0) end.

(умножение на 1.0 в выражении

(l.O*MaxSize*MaxSize+MaxSize)/2

необходимо для приведения операндов к вещественному типу; если этого не сделать, возникнет переполнение значения типа Word и будет напечатан неверный результат).

Расширитель ДОС для каждого загружаемого сегмента кода, который, как известно, не может изменяться, создает селектор—псевдоним, ссылающийся на тот же сегмент кода, но определенный как сегмент данных. Селектор—псевдоним располагается в таблице дескрипторов сразу за селектором кода, поэтому получить к нему доступ можно с помощью той же переменной Selectorlnc. Это открывает возможность создания самоизменяющихся программ. Если, например, требуется изменить 10—и по счету байт в коде процедуры МуРгос, можно использовать такой фрагмент программы:

var

Р: Pointer;

Р := Ptr(Seg(МуРгос)+SelectorInc, Ofs(MyProc));

Byt^GetPtrtPylO)'') := ....

В защищенном режиме программа не может ссылаться на модуль Overlay, использовать директивы {$0 . . . } и переменные OvrXXXX из модуля System, так как расширитель ДОС автоматически реализует все функции администратора оверлея. Для управления свойством выгружае— мости кодовых сегментов в среду ВР введена специальная директива компилятора {$S Flagi Flag]). В качестве параметров Flag в этой директиве должны использоваться следующие идентификаторы:

 

Идентификатор

Назначение

MOVEABLE

Сегмент может перемещаться в памяти

FIXED

Сегмент не может перемещаться

PRELOAD

Сегмент загружается в момент старта программы

DEMANLOAD

Сегмент загружается только при обращении к нему

DISCARDABLE

Сегмент можно удалять из памяти

PERMANENT

Сегмент нельзя удалять из памяти


По умолчанию каждый модуль компилируется с такой директивой:

{$S MOVEABLE DEMANLOAD DISCARDABLE}

Это означает, что соответствующий кодовый сегмент может перемещаться в памяти, загружается только когда ему передается управление и он может удаляться из памяти. При необходимости Вы можете переопределить умалчиваемые свойства, включив директиву $3 в самом начале программы или модуля. Если в директиву вставляются два противоположных по смыслу идентификатора, действует последний из них.

 

Глава 9

Оглавление 

На первую страницу

Rambler's Top100 Rambler's Top100
PROext: Top 1000

(с)Все права защищены

По всем интересующим вопросам прошу писать на электронный адрес

Hosted by uCoz