Logo Море(!) аналитической информации!
IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware
Скидка до 20% на услуги дата-центра. Аренда серверной стойки. Colocation от 1U!

Миграция в облако #SotelCloud. Виртуальный сервер в облаке. Выбрать конфигурацию на сайте!

Виртуальная АТС для вашего бизнеса. Приветственные бонусы для новых клиентов!

Виртуальные VPS серверы в РФ и ЕС

Dedicated серверы в РФ и ЕС

По промокоду CITFORUM скидка 30% на заказ VPS\VDS

VPS/VDS серверы. 30 локаций на выбор

Серверы VPS/VDS с большим диском

Хорошие условия для реселлеров

4VPS.SU - VPS в 17-ти странах

2Gbit/s безлимит

Современное железо!

Глава 17. Программирование в защищенном режиме DOS

             Микропроцессор 80286 дает новый способ адресации  к  памяти:
        защищенный режим  виртуальной адресации или просто защищенный ре-
        жим. Этот новый режим адресации дает три основных преимущества:

             * Адресация к памяти объемом до 16 мегабайт.

             * Логическое адресное пространство, превышающее пространство
               физических адресов.

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

             С помощью Borland Pascal вы легко можете писать работающие в
        защищенном режиме прикладные программы DOS без необходимости при-
        менения дополнительного  "расширителя"  DOS.  Вы обнаружите,  что
        многие программы реального режима прекрасно работают в защищенном
        режиме. Данная глава поможет вам модифицировать те программы, ко-
        торые этого не делают,  и прояснит некоторые основные моменты за-
        щищенного режима и его отличия от реального режима.


Что такое защищенный режим?

Процессор 80286 и более поздние процессоры поддерживают два режима операций: защищенный режим и реальный режим. Реальный ре- жим совместим с работой процессора 8086 и позволяет прикладной программе адресоваться к памяти объемом до одного мегабайта. За- щищенный режим расширяет диапазон адресации до 16 мегабайт. Ос- новное отличие между реальным и защищенным режимом заключается в способе преобразования процессором логических адресов в физичес- кие. Логические адреса - это адреса, используемые в прикладной программе. Как в реальном, также и в защищенном режиме логический адрес - это 32-разрядное значение, состоящее из 16-битового се- лектора (адреса сегмента) и 16-битового смещения. Физические ад- реса - это адреса, которые процессор использует для обмена данны- ми с компонентами системной памяти. В реальном режиме физический адрес представляет собой 20-битовое значение, а в защищенном ре- жиме - 24-битовое. Когда процессор обращается к памяти (для выборки инструкции или записи переменной), он генерирует из логического адреса физи- ческий адрес. В реальном режиме генерация физического адреса сос- тоит из сдвига селектора (адреса сегмента) на 4 бита влево (это означает умножение на 16) и прибавления смещения. Полученный в результате 20-разрядный адрес используется затем для доступа к памяти. 16Мб----------------- ¦ ¦ --------- ¦ ¦ ¦Смещение+- ¦ ¦ L--------- ¦ ¦ ¦ ¦ +----------------+ L--+----->----------¦+ сегмент 64К -->+----------------+- ¦ ¦ ¦ ¦ ¦ Пространство ¦ --------- ------- ¦ ¦ адресов ¦ ¦Селектор+-+ x 16 +------ ¦ ¦ L--------- L------- ¦ ¦ 0L----------------- Рис. 17.1 Генерация физического адреса в реальном режиме. Чтобы получить физический адрес в защищенном режиме, селек- торная часть логического адреса используется в качестве индекса таблицы дескрипторов. Запись в таблице дескрипторов содержит 24-битовый базовый адрес, к которому затем для образования физи- ческого адреса прибавляется смещение логического адреса. 16Мб----------------- ¦ ¦ --------- ¦ ¦ ¦Смещение+- ¦ ¦ L--------- ¦ ¦ ¦ ¦ +----------------+ Таблица дескрипторов L--+----->----------¦+ сегмент 64К ------- -->+----------------+- +------+ ¦ ¦ ¦ +------+ ¦ ¦ Пространство ¦ +------+ ¦ ¦ адресов ¦ -->+------+---- ¦ ¦ ¦ +------+ ¦ ¦ ¦ +------+ 0L----------------- ¦ +------+ ¦ +------+ ¦ +------+ ¦ +------+ ¦ +------+ ¦ +------+ ¦ +------+ ¦ +------+ --------- ¦ +------+ ¦Селектор+-- L------- L--------- Рис. 17.2 Генерация физического адреса в защищенном режиме. Каждая запись в таблице дескрипторов называется дескриптором и определяет сегмент в памяти. Запись таблицы дескрипторов зани- мает 8 байт, а записанная в дескрипторе информация включает в се- бя базовый адрес, предельное значение и флаги полномочий доступа к сегменту. Записи предельного значения сегмента и полномочий доступа в дескрипторе определяют размер и тип сегмента. Сегменты могут иметь размер от 1 до 65536 байт и могут быть сегментами кода или сегментами данных. Сегменты кода могут содержать выполняемые ма- шинные инструкции и доступные только по чтению данные. Сегменты данных могут содержать данные, доступные по чтению и записи. За- писывать данные в сегменты кода или выполнять инструкции в сег- ментах данных невозможно. Любая попытка сделать это или попытка доступа к данным вне границ сегмента вызывает общий сбой по на- рушению защиты (сокращенно сбой GP). Поэтому режим и называется защищенным. По данному адресу в реальном режиме прикладная программа мо- жет определить физический адрес. В защищенном режиме это обычно не так, поскольку селекторная часть логического адреса является индексом в таблице дескрипторов, и сам селектор не имеет прямого отношения к вычислению физического адреса. Это дает то преиму- щество, что управление виртуальной памятью можно реализовать, не влияя на прикладную программу. Например, путем простого обновле- ния поля базового адреса дескриптора сегмента, операционная сис- тема может перемещать сегмент в физической памяти без влияния на использующую сегмент прикладную программу. Прикладная программа ссылается только на селектор сегмента, и на селектор не влияют изменения в дескрипторе. Прикладная программа редко имеет дело с дескрипторами. При необходимости дескрипторы создаются и уничтожаются операционной системой и администратором памяти, а прикладная программа знает о соответствующих селекторах. Селекторы аналогичны описателям фай- лов - с точки зрения прикладной программы это то, что обслужива- ется операционной системой, но в операционной системе они работа- ют как индексы содержащих дополнительную информацию таблиц.

Расширения Borland защищенного режима DOS

Расширения защищенного режима Borland Pascal реализованы че- рез два компонента: DPMI-сервер (файл DPMI16BI.OVL) и администра- тор этапа выполнения (файл RTM.EXE).

DPMI-сервер

Интерфейс защищенного режима DOS (DPMI) - это отраслевой стандарт, позволяющий программам DOS аппаратно-независимым путем получить доступ к развитым средствам персональных компьютеров, реализованных на процессорах 80286, 80386 и 80486. Определены функции DPMI для обслуживания таблиц дескрипторов, переключения режима, распределения расширенной памяти, выделения памяти DOS, управления подсистемой прерываний и взаимодействия с программами реального режима. Расширения защищенного режима Borland Pascal основаны на спецификации DPMI 0.9. Хотя спецификация DPMI не поддерживает вы- зовы DOS из прикладных программ защищенного режима, DPMI-сервер Borland и серверы многих других фирм, включая улучшенный режим Windows 3.x, поддерживают прерывание INT 21H и другие стандартные прерывания DOS и BIOS, используемые обычно в приложениях DOS за- щищенного режима.

Администратор этапа выполнения

Администратор этапа выполнения (RTM.EXE) является надстрой- кой DPMI-сервера и обеспечивать для прикладных программ защищен- ного режима несколько служебных функций. Администратор этапа вы- полнения содержит загрузчик защищенного режима и администратор памяти защищенного режима и позволяет под DPMI сосуществовать нескольким клиентам защищенного режима. Приложения защищенного режима Borland используют те же фор- маты выполняемых файлов, что и Windows 3.x и OS/2 1.x. Программ- ный загрузчик администратора этапа выполнения может загружать как выполняемые файлы (.EXE), так и динамически компонуемые библиоте- ки (.DLL). Администратор памяти защищенного режима позволяет прикладным программам защищенного режима распределять блоки динамической па- мяти. Администратор памяти поддерживает фиксированные, перемещае- мые и выгружаемые блоки, а также обслуживает код и сегменты дан- ных прикладной программы. Используя уникальные для защищенного режима средства, администратор памяти функционирует также в ка- честве администратора оверлеев, автоматически загружая и выгружая сегменты кода (по этой причине прикладной программе защищенного режима не требуется модуль Overlay). Прикладные программы могут получить доступ к программам за- щищенного режима через модуль WinAPI. Модуль WinAPI, описанный в следующем разделе, реализует подмножество функций API (прикладно- го программного интерфейса) Windows, обеспечивая управление па- мятью, обслуживание программных модулей, управление ресурсами, загрузку динамически компонуемых библиотек и доступ к селекторам на нижнем уровне. Поскольку администратор этапа выполнения API является подмножеством API Windows, вы можете написать совмести- мые на уровне двоичного кода динамически компонуемые библиотеки, которые можно использовать и в защищенном режиме DOS, и в Windows.

Разработка прикладных программ DOS защищенного режима

Написание прикладной программы защищенного режима не предс- тавляет собой сложной задачи. Вам не нужно беспокоиться о селек- торах и адресах памяти. Операционная система с расширениями Borland все делает за вас. Фактически, большинство ваших программ реального режима может прекрасно работать в защищенном режиме. В следующих разделах описывается некоторая разница между реальным и защищенным режимом, о которых вы должны знать при разработке прикладной программы защищенного режима.

Надежное программирование в защищенном режиме

Существует несколько приемов, используемых обычно в програм- мах реального режима, которые в программах защищенного режима бу- дут приводить к общему нарушению защиты (сбой GP). Borland Pascal при сбое GP выводит ошибку этапа выполнения 216. Сбой GP происхо- дит, когда вы пытаетесь получить доступ к памяти, к которой ваша прикладная программа обращаться не может. Операционная система останавливает прикладную программу, но сбоя системы не происхо- дит. Хотя сбои GP и прекращают работу вашей программы, система "защищена" от сбоя. К сбою GP приводит следующее: * загрузка в сегментные регистры недопустимых значений; * обращение к памяти вне границы сегмента; * запись в сегмент кода; * разыменование указателей nil. Примечание: Сбои по нарушению защиты предохраняют вашу систему от плохой практики программирования.

Загрузка в сегментные регистры недопустимых значений

Когда процессор работает в защищенном режиме, сегментные ре- гистры (CS, DS, ES и SS) могут содержать только селекторы. Пос- кольку селекторы являются индексами в таблице дескрипторов, они не имеют физического отношения к памяти, на которую ссылается. Если вы пытаетесь загрузить в сегментный регистр произвольное значение, то возможно получите сбой GP, поскольку это значение может не представлять допустимого дескриптора.

Функция Ptr и массивы Mem

При разыменовании указателей компилятор генерирует код для загрузки сегментного регистра. Если вы строите указатели с по- мощью стандартной функции Ptr, то нужно обеспечить, чтобы сег- ментная часть указателя была допустимым селектором. Аналогично, при работе с массивами Mem, MemW и MemL вы вместо физических ад- ресов должны использоваться селекторы. Например, при доступе к рабочей области ROM BIOS (сегмент $0040) или к областям видеопа- мяти (сегменты $A000, $B000 и $B800) следует использовать вместо абсолютных значений переменные SegXXXX. (Переменные SegXXXX опи- сываются ниже.)

Абсолютные переменные

В защищенном режиме вы не можете задавать абсолютный адрес переменной. Любой исходных код, где сегмент и смещение задаются в операторе absolute, нужно переписать. Например, вам может потре- боваться построить указатель, используя переменные SegXXXX.

Операции с сегментами

Добавление или вычитание значений из селекторной части ука- зателя обычно не допускается. Например, добавление к селекторной части указателя $1000 в реальном режиме увеличивает указатель на 64К, но в защищенном режиме результирующий указатель будет недо- пустимым. Вместо этого для выделения и управления блоками памяти следует использовать функцию GlobalXXXX модуля WinAPI. В Borland Pascal существует способ выполнения арифметических операций с селекторами с помощью переменной SelectorInc (см. ни- же).

Использование сегментных регистров в качестве временных переменных

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

Доступ к памяти вне границ сегмента

В реальном режиме каждый сегмент имеет размер 64К. В защи- щенном режиме дескриптор сегмента содержит поле, специфицирующее предельный размер сегмента, и если вы пытаетесь обратиться к дан- ным вне границ сегмента, по получите сбой GP. При загрузке прик- ладной программы администратор этапа выполнения устанавливает со- ответствующие предельные значения для сегментов кода, данных и стека. Кроме того, блок памяти, распределяемый с помощью функции GlobalAlloc модуля WinAPI, имеет предельное значение сегмента, соответствующее размеру блока памяти.

Запись в сегмент кода

В реальном режиме можно записывать переменные в сегмент ко- да, поскольку реальные режим не определяет, что может и что не может существовать в сегменте. В защищенном режиме это не так. Селектор защищенного режима имеет флаг чтения/записи или доступа только по чтению, а селекторы кода всегда отмечены как доступные только по чтению. Если вы пытаетесь записывать в селектор сегмен- та кода, происходит сбой GP. Однако вы можете использовать псев- доним и написать самомодифицирующийся код (см. ниже).

Разыменование указателей nil

При преобразовании прикладной программы реального режима в защищенный режим, в программе, которая уже годы работала без оши- бок, возможно внезапное появление определенных ошибок. Например, вы можете случайно разыменовывать указатель nil, или обнаружите, что ваша программа содержит "потерянные" указатели, которые разы- меновываются после их освобождения. В реальном режиме такие ошиб- ки не обязательно проявляются, но в защищенном режиме они обычно приводят к сбою GP. Согласно своему названию, защищенный режим значительно лучше предохраняет вас от ошибок, связанных с указа- телями.

Сегменты кода и данных

Аналогично программе Borland Pascal реального режима, прог- рамма защищенного режима содержит несколько сегментов кода, сег- мент данных и сегмент стека. При загрузке программы защищенного режима администратор этапа выполнения автоматически выделяет се- лекторы для сегментов кода, данных и стека. Для сегментов кода с помощью директивы компилятора $C можно управлять отдельными ат- рибутами. В частности, сегменты кода можно сделать перемещаемыми или фиксированными в физической памяти, они могут загружаться предварительно или по запросу, а также могут быть выгружаемыми или постоянными. Примечание: Подробнее о директиве компилятора $C расс- казывается в Главе 21 данного руководства и в Главе 2 ("Ди- рективы компилятора") "Справочного руководства программис- та". Атрибуты сегмента кода позволяют вам обозначать сегмент как статический (перемещаемый, предварительно загружаемый, постоян- ный) или динамический (перемещаемый, загружаемый по запросу, выг- ружаемый). Таким образом, в защищенном режиме вам не нужно ис- пользовать модуль Overlay и директиву компилятора $O, и в версии модуля System для защищенного режима переменные OvrXXXXXX отсутс- твуют.

Управление динамически распределяемой памятью

Администратор динамически распределяемой области памяти Borland Pascal защищенного режима довольно существенно отличается от администратора динамически распределяемой памяти Borland Pascal реального режима. В частности, переменные HeapOrg, HeapEnd, HeapPtr и FreeList в версии модуля System для защищенно- го режима не определены. Администратор этапа выполнения динами- чески распределяемой области памяти Borland Pascal защищенного режима (который идентичен администратору этапа выполнения динами- чески распределяемой области памяти Borland Pascal для Windows) для выполнения основных операций по выделению и освобождению па- мяти использует администратор этапа выполнения, а для оптимизации распределения небольших блоков памяти включает в себя подсистему вторичного распределения сегмента. Подробнее об администраторе динамически распределяемой области памяти этапа выполнения расс- казывается в Главе 21.

Предопределенные селекторы

В модуле System для обычно используемых адресов реального режима предусмотрено несколько предопределенных селекторов. Они именуются по физическому сегменту, которому данные селекторы присвоены, и используются для совместимости между реальным и за- щищенным режимом DOS. Предопределенные селекторы Таблица 17.1 -------------------T-------------------------------------------- ¦ Селектор ¦ Описание ¦ +------------------+--------------------------------------------+ ¦ Seg0040 ¦ Используется для доступа к области данных¦ ¦ ¦ BIOS $40 в младших адресах. ¦ +------------------+--------------------------------------------+ ¦ SegA000 ¦ Используется для доступа к графической па-¦ ¦ ¦ мяти EGA и VGA по адресу сегмента $A000. ¦ +------------------+--------------------------------------------+ ¦ SegB000 ¦ Используется для доступа к видеопамяти мо-¦ ¦ ¦ нохромного адаптера по адресу сегмента¦ ¦ ¦ $A000. ¦ +------------------+--------------------------------------------+ ¦ SegB800 ¦ Используется для доступа к видеопамяти¦ ¦ ¦ цветного графического адаптера по адресу¦ ¦ ¦ сегмента $A000. ¦ L------------------+--------------------------------------------- В реальном режиме переменные SegXXXX всегда содержат значе- ния $0040, $A000, $B000 и $B800 соответственно. В защищенном ре- жиме код запуска библиотеки исполняющей системы создает четыре селектора, ссылающихся на конкретные области памяти реального ре- жима. При ссылке на эти области памяти вам следует использовать переменные SegXXXX. Например, если у вас был код следующего вида: CtrMode := Mem[$40: $49]; то вместо него следует записать: CtrMode := Mem[Seg0040: $49]; Используя переменные SegXXXX, вы можете гарантировать, что ваша программа без изменений будет работать в реальном и защищен- ном режимах.

Переменная SelectorInc

Переменная SelectorInc модуля System содержит значение, ко- торое должно прибавляться к селектору или вычитаться из него для получения следующего или предыдущего селектора в таблице дескрип- торов. SelectorInc полезно использовать при работе с большими блоками памяти (превышающими 64К) и при доступе к псевдонимам сегментов. Для выделения блоков, превышающих 64К (такие блоки называют также большими блоками памяти), можно использовать функции GlobalAlloc и GlobalAllocPrt в модуле WinAPI. Большие блоки неп- рерывны в физической памяти, но из-за 16-разрядной архитектуры процессора прикладная программа не может получить к ним доступ целиком. Для большого блока памяти администратор памяти выделяет несколько непрерывных (следующих подряд) селекторов, каждый из которых (кроме последнего) ссылается на часть большого блока па- мяти размером 64К. Например, чтобы выделить блока памяти размером в 220К, администратор памяти создает четыре селектора, при этом первые три селектора ссылаются на блоки по 64К, а последний се- лектор - на блок размером 28К. Прибавляя SelectorInc к селектору, принадлежащему большому блоку, вы можете получить селектор для следующего сегмента, а вычитая SelectorInc - для предыдущего. При распределении большого блока функция GlobalAlloc всегда возвращает описатель первого сегмента, а GlobalAllocPtr - указа- тель на первый сегмент. Приведенная ниже функция GetPtr воспринимает указатель боль- шого блока (возвращаемый функцией GlobalAllocPtr) и 32-разрядное смещение и возвращает указатель на заданное внутри блока смеще- ние. function GetPtr(P: Pointer; Offset: Longint): Pointer; type Long = record Lo, Hi: Word; end; begin GetPtr := Ptr( Long(P).Hi + Long(Offset).Hi * SelectorInc, Long(P).Lo + Long(Offset).Lo); end; Заметим, что старшее слово параметра Offset используется для определения того, сколько раз нужно увеличить селекторную часть P для получения корректного сегмента. Например, если Offset равно $24000, то селекторная часть P будет увеличена на 2 * SelectorInc, а смещение P - на $4000. Следующая функция LoadFile загружает в блок памяти весь файл и возвращает указатель на блок. Если файл превышает 64К, то выде- ляется большой блок памяти. function LoadFile(const FileName: string): Pointer; var Buffer: Pointer; Size, Offset, Count: Longint; F: file; begin Buffer := nil; Assign(F, FileName); Reset(F, 1); Size := FileSize(F); Buffer := GlobalAllocPtr(gmem_Moveable, Size); if Buffer <> nil then begin Offset := 0; while Offset < Size do begin Count := Size - Offset; if Count > $8000 then Count := $8000; BlockRead(F, GetPtr(Buffer, Offset)^, Count); Inc(Offset, Count); end; end; LoadFile := Buffer; end; Переменная SelectorInc определена также в версии модуля System для реального режима. В реальном режиме она всегда содер- жит значение $1000, которое при сложении его с сегментной частью указателя реального режима увеличивает указатель на 64К. Другим образом вы можете использовать переменную SelectorInс только в программах DOS защищенного режима. Используйте перемен- ную SelectorInc для доступа к псевдонимам сегментов, выделяемых администратором этапа выполнения при загрузке прикладной програм- мы. Для каждого сегмента кода прикладной программы администратор этапа выполнения создает селектор-псевдоним, ссылающийся на тот же сегмент, но имеющий полномочия селектора данных. Для сегментов стека и данных селекторы-псевдонимы не создаются. Чтобы получить доступ к селектору-псевдониму для конкретного сегмента, добавьте к селектору сегмента SelectorInc. Предположим, например, что P - это переменная типа Pointer, а Foo - процедура или функция. Тогда присваивание вида: P := Addr(Foo) приводит к тому, что P будет указывать на выполняемую доступную только по чтению точку входа Foo, а после оператора: P := Ptr(Seg(Foo) + SelectorInc, Ofs(Foo)); P будет ссылаться на тот же адрес, но с полномочиями на чте- ние/запись.

Модуль WinAPI

Модуль WinAPI дает вам непосредственный доступ к расширениям Borland защищенного режима DOS. Чтобы облегчить написание перено- симых прикладных программ и совместимых на уровне двоичного кода DLL, разработан интерфейс WinAPI, являющийся подмножеством интер- фейса API Windows. Модуль WinAPI позволяет вам использовать функции управления памятью, управления ресурсами, модулями, селекторами и многие другие функции API. Ниже приведено их краткое описание. Полное описание констант, типов, процедур и функций модуля WinAPI вы мо- жете найти в "Справочном руководстве программиста". При работе под Windows подпрограммы API, поддерживаемые с помощью модуля WinAPI, находятся в динамически компонуемых библи- отеках KERNEL.DLL и USER.DLL. В защищенном режиме DOS эти DLL не требуются, так как администратор этапа выполнения защищенного ре- жима содержит реализацию подпрограмм KERNEL и USER, автоматичес- ки перенаправляя их вызовы администратору.

Управление памятью

При разработке программ, работающих с динамической памятью, обычно используются стандартные процедуры New, Dispose, GetMem и FreeMem. Однако получить доступ к администратору памяти защищен- ного режима Borland вы можете с помощью функций GlobalXXXX в мо- дуле WinAPI. Заметим, что функции GlobalXXXXPtr комбинируют в одной подп- рограмме общие последовательности вызовов функций, такие как GlobalAlloc, за которыми следуют вызовы GlobalLock, GlobalUnlock или GlobalFree.

Подпрограммы управления памятью API

Таблица 17.2 ----------------------T----------------------------------------- ¦ Функция ¦ Описание ¦ +---------------------+-----------------------------------------+ ¦ GetFreeSpace ¦ Определяет объем свободной памяти в ди-¦ ¦ ¦ намически распределяемой области. ¦ +---------------------+-----------------------------------------+ ¦ GlobalAlloc ¦ Выделяет блок памяти в динамически расп-¦ ¦ ¦ ределяемой области. ¦ +---------------------+-----------------------------------------+ ¦ GlobalAllocPtr ¦ Выделяет и блокирует блок памяти (с по-¦ ¦ ¦ мощью вызовов GlobalAlloc и GlobalLock).¦ ¦ ¦ ¦ +---------------------+-----------------------------------------+ ¦ GlobalCompact ¦ Переупорядочивает память, распределен-¦ ¦ ¦ ную в динамической области, так что ос-¦ ¦ ¦ вобождается заданный объем памяти. ¦ +---------------------+-----------------------------------------+ ¦ GlobalDiscard ¦ Выгружает заданный объект памяти. ¦ +---------------------+-----------------------------------------+ ¦ GlobalDosAlloc ¦ Распределяет память, к которой можно по-¦ ¦ ¦ лучить доступ в реальном режиме DOS. Эта¦ ¦ ¦ память будет существовать в первом мега-¦ ¦ ¦ байте линейного адресного пространства. ¦ +---------------------+-----------------------------------------+ ¦ GlobalDosFree ¦ Освобождает память, выделенную ранее с¦ ¦ ¦ помощью GlobalDosAlloc. ¦ +---------------------+-----------------------------------------+ ¦ GlobalFlags ¦ Получает информацию о блоке памяти. ¦ +---------------------+-----------------------------------------+ ¦ GlobalFree ¦ Освобождает разблокированный блок памяти¦ ¦ ¦ и делает его описатель недействительным.¦ +---------------------+-----------------------------------------+ ¦ GlobalFreePtr ¦ Разблокирует и освобождает блок памяти¦ ¦ ¦ с помощью GlobalUnlock и GlobalFree. ¦ +---------------------+-----------------------------------------+ ¦ GlobalHandle ¦ Получает описатель объекта в памяти по¦ ¦ ¦ заданному адресу сегмента. ¦ +---------------------+-----------------------------------------+ ¦ GlobalLock ¦ Увеличивает счетчик ссылки блока памяти¦ ¦ ¦ и возвращает указатель на него. ¦ +---------------------+-----------------------------------------+ ¦ GlobalLockPtr ¦ То же, что и GlobalLock, но вместо опи-¦ ¦ ¦ сателя воспринимает указатель. ¦ +---------------------+-----------------------------------------+ ¦ GlobalLRUNewest ¦ Перемещает объект в памяти на новую не-¦ ¦ ¦ давно используемую позицию, минимизируя,¦ ¦ ¦ таким образом, вероятность выгрузки¦ ¦ ¦ объекта. ¦ +---------------------+-----------------------------------------+ ¦ GlobalLRUOldest ¦ Перемещает объект в памяти на самую¦ ¦ ¦ "старую" недавно используемую позицию,¦ ¦ ¦ максимизирую вероятность выгрузки объ-¦ ¦ ¦ екта. ¦ +---------------------+-----------------------------------------+ ¦ GlobalNorify ¦ Вызывает адрес экземпляра процедуры уве-¦ ¦ ¦ домления, передавая описатель блока, ко-¦ ¦ ¦ торый нужно выгрузить. ¦ +---------------------+-----------------------------------------+ ¦ GlobalPageLock ¦ Увеличивает значение счетчика блокиров-¦ ¦ ¦ ки для памяти, связанной с данным селек-¦ ¦ ¦ тором. ¦ +---------------------+-----------------------------------------+ ¦ GlobalPageUnlock ¦ Уменьшает значение счетчика блокировки¦ ¦ ¦ для памяти, связанной с данным селекто-¦ ¦ ¦ ром. ¦ +---------------------+-----------------------------------------+ ¦ GlobalPtrHandle ¦ По заданному указателю на блок памяти¦ ¦ ¦ возвращает описатель этого блока. ¦ +---------------------+-----------------------------------------+ ¦ GlobalReAlloc ¦ Перераспределяет блок памяти. ¦ +---------------------+-----------------------------------------+ ¦ GlobalReAllocPtr ¦ Разблокирует, перераспределяет и блоки-¦ ¦ ¦ рует блок памяти (используя функции¦ ¦ ¦ GlobalUnlock, GlobalReAlloc и¦ ¦ ¦ GlobalLock). ¦ +---------------------+-----------------------------------------+ ¦ GlobalSize ¦ Определяет текущий размер блока памяти. ¦ +---------------------+-----------------------------------------+ ¦ GlobalUnfix ¦ Разблокирует блок памяти, блокированный¦ ¦ ¦ ранее с помощью GlobalLock. ¦ +---------------------+-----------------------------------------+ ¦ GlobalUnockPtr ¦ То же, что и GlobalUnlock, но вместо¦ ¦ ¦ описателя воспринимает указатель. ¦ +---------------------+-----------------------------------------+ ¦ LockSegment ¦ Блокирует заданный выгружаемый сегмент. ¦ +---------------------+-----------------------------------------+ ¦ UnlockSegment ¦ Разблокирует сегмент. ¦ L---------------------+------------------------------------------ Функция GlobalAlloc используется для распределения блоков памяти. Для их освобождения применяется функция GlobalFree. Адми- нистратор памяти поддерживает три типа блоков памяти: фиксирован- ный, перемещаемый и выгружаемый. Фиксированный блок остается в одних и тех же адресах физической памяти. Перемещаемый блок может перемещаться в физической памяти и освобождать место для других запросов на выделение памяти, а выгружаемые блоки могут выгру- жаться из памяти, освобождая место для других блоков. С помощью передаваемых GlobalAlloc флагов вы можете выбрать один из этих трех типов: * gmem_Fixed (фиксированный) * gmem_Moveable (перемещаемый) * gmem_Moveable + gmem_Discardable (выгружаемый) Прикладная программа обычно выделяет только перемещаемые блоки памяти, которые представляются типом THandle в модуле WinAPI. Описатель памяти - это значение размером в слово, которое идентифицирует блок памяти аналогично тому, как описатель файла - это значение размером в слово, идентифицирующее открытый файл. Перед тем как вы сможете получить доступ к памяти, его нужно заблокировать с помощью функции GlobalAlloc, а когда вы закончите к нему обращаться, его нужно разблокировать с помощью функции GlobalUnlock. GlobalLock возвращает полный 32-разрядный указатель на первый байт блока. Смещение указателя всегда равно 0. В защи- щенном режиме DOS селектор указателя - это тоже самое, что описа- тель блока, но в Windows это не всегда так. Правильная последовательность вызовов для выделения, блоки- ровки, разблокировки или освобождения блока показана в приведен- ном ниже примере. В данном примере H - это переменная типа THandle, а P - указатель: H := GlobalAlloc(gmem_Moveable, 8192); { выделение блока } if H <> then { если память выделена } begin P := GlobalLock(H); { блокировка блока } . . { доступ к блоку через P } . GlobalUnlock(H); { разблокировать блок } GlobalFree(H); { освободить блок } end; Блокировка и разблокировка блока при каждом обращении к нему достаточно утомительна и ведет к ошибкам, и реально она необходи- ма только для выгружаемых блоков и в прикладных программах Windows, работающих в реальном режиме. Во всех других ситуациях лучшим решением является блокировка блока сразу после его выделе- ния и сохранение этого состояния до освобождения блока. С этой целью модуль WinAPI включает в себя семейство подпрограмм-"оболо- чек" GlobalXXXXPtr. Особый интерес представляет функция GlobalAllocPtr, которая выделяет и блокирует блок памяти, и функ- ция GlobalFreePtr, разблокирующая и освобождающая блок памяти. С помощью этих подпрограмм приведенный выше пример можно упростить: H := GlobalAlloc(gmem_Moveable, 8192); { выделение блока } if H <> then { если память выделена } begin . . { доступ к блоку } . GlobalFreePtr(P); { освободить блок } end; Вызвав функцию GlobalReAlloc, вы можете изменить размер или атрибуты блока памяти, сохранив его содержимое. Функция GlobalReAlloc возвращает новый описатель блока, который может от- личаться от передаваемого функции описателя, если старый размер или новый размер блок превышает 64К. Заметим, что в тех случаях, когда старый размер блока и новый его размер меньше 64К, GlobalReAlloc всегда может изменить размер блока, не изменяя его описателя. Функция GlobalReAlloc можно также использоваться для измене- ния атрибутов блока. Это можно сделать, задав наряду с gmem_Moveable или gmem_Discardable флаг gmem_Modify. Функция GlobalReAlloc выполняет те же действия, что и GlobalReAlloc, но обе они вместо описателей использует указатели. Имеется также ряд других, менее часто используемых GlobalXXXX. Все они подробно описаны в Главе 1 ("Справочник по библиотеке") "Справочного руководства программиста".

Управление модулем

Администратор этапа выполнения поддерживает следующие подп- рограммы обслуживания модулей: Подпрограммы API обслуживания модулей Таблица 17.3 ----------------------------T----------------------------------- ¦ Подпрограмма ¦ Описание ¦ +---------------------------+-----------------------------------+ ¦ FreeLibrary ¦ Делает недействительным загружен-¦ ¦ ¦ ный модуль библиотеки, и освобож-¦ ¦ ¦ дает соответствующую память, если¦ ¦ ¦ ссылок на модуль больше нет. ¦ +---------------------------+-----------------------------------+ ¦ GetModuleFileName ¦ Дает полный маршрут и имя выполня-¦ ¦ ¦ емого файла, задающий, откуда заг-¦ ¦ ¦ ружен модуль. ¦ +---------------------------+-----------------------------------+ ¦ GetModuleHandle ¦ Определяет описатель заданного мо-¦ ¦ ¦ дуля. ¦ +---------------------------+-----------------------------------+ ¦ GetModuleUsage ¦ Определяет счетчик ссылок на мо-¦ ¦ ¦ дуль. ¦ +---------------------------+-----------------------------------+ ¦ GetProcAddress ¦ Определяет адрес экспортируемой¦ ¦ ¦ библиотечной функции. ¦ +---------------------------+-----------------------------------+ ¦ LoadLibrary ¦ Загружает указанный библиотечный¦ ¦ ¦ модуль. ¦ L---------------------------+------------------------------------ Некоторые из этих подпрограмм воспринимают в качестве пара- метра описатель модуля. Описатель модуля самой прикладной прог- раммы хранится в переменной HInstance, описанной в модуле System.

Управление ресурсами

Администратор этапа выполнения поддерживает следующие подп- рограммы управления ресурсами: Функции API управления ресурсами Таблица 17.4 -----------------------T---------------------------------------- ¦ Функция ¦ Описание ¦ +----------------------+----------------------------------------+ ¦ AccessResource ¦ Открывает заданный выполняемый файл и¦ ¦ ¦ перемещает указатель файла на начало¦ ¦ ¦ заданного ресурса. ¦ +----------------------+----------------------------------------+ ¦ FindResource ¦ Определяет адрес ресурса в заданном¦ ¦ ¦ файле ресурса. ¦ +----------------------+----------------------------------------+ ¦ FreeResource ¦ Уменьшает счетчик ссылок для загружен-¦ ¦ ¦ ного ресурса. Когда значение этого¦ ¦ ¦ счетчика становится равным нулю, то ис-¦ ¦ ¦ пользуемая ресурсом память освобождает-¦ ¦ ¦ ся. ¦ +----------------------+----------------------------------------+ ¦ LoadResource ¦ Загружает заданный ресурс в память. ¦ +----------------------+----------------------------------------+ ¦ LoadString ¦ Загружает заданную строку ресурса. ¦ +----------------------+----------------------------------------+ ¦ LockResource ¦ Блокирует заданный ресурс в памяти и¦ ¦ ¦ увеличивает его счетчик ссылок. ¦ +----------------------+----------------------------------------+ ¦ SizeOfResource ¦ Возвращает размер (в байтах) заданного¦ ¦ ¦ ресурса. ¦ +----------------------+----------------------------------------+ ¦ UnlockResource ¦ Разблокирует заданный ресурс и уменьша-¦ ¦ ¦ ет на 1 счетчик ссылок на ресурс. ¦ L----------------------+----------------------------------------- Ресурсы могут компоноваться с прикладной программой с по- мощью директив компилятора {$R имя_файла}. Указанные файлы должны быть файлами ресурсов Windows (.RES). Обычно с прикладными прог- раммами защищенного режима DOS компонуются только строковые ре- сурсы и ресурсы, определенные пользователем. Другие типы ресурсов Windows к прикладной программе DOS обычно неприменимы. Примечание: Ресурсы Turbo Vision не следуют тем же соглашениям, что ресурсы Windows, и к ним нельзя обращаться с помощью подпрограмм API. Некоторые подпрограммы API управления ресурсами требуют ука- зания описателя экземпляра, которым обычно является указатель эк- земпляра прикладной программы (который содержится в переменной HInstance модуля System).

Управление селектором

Прикладной программе обычно не требуется манипулировать се- лекторами, но в отдельных ситуациях полезно использовать следую- щие подпрограммы обслуживания селектора: Подпрограммы API управления селектором Таблица 17.5 ------------------------T--------------------------------------- ¦ Функция ¦ Описание ¦ +-----------------------+---------------------------------------+ ¦ AllocDStoCSAlias ¦ Отображает селектор сегмента данных на¦ ¦ ¦ селектор сегмента кода. ¦ +-----------------------+---------------------------------------+ ¦ AllocSelector ¦ Выделяет новый селектор. ¦ +-----------------------+---------------------------------------+ ¦ ChangeSelector ¦ Генерирует селектор кода, соответству-¦ ¦ ¦ щий заданному селектору данных, или¦ ¦ ¦ генерирует заданный селектор, соот-¦ ¦ ¦ ветствующий селектору кода. ¦ +-----------------------+---------------------------------------+ ¦ FreeSelector ¦ Освобождает селектор, первоначально¦ ¦ ¦ выделенный функциями AllocDStoCSAlias¦ ¦ ¦ или AllocSelector. ¦ +-----------------------+---------------------------------------+ ¦ GetSelectorBase ¦ Дает базовый адрес селектора. ¦ +-----------------------+---------------------------------------+ ¦ GetSelectorLimit ¦ Возвращает предельное значение для за-¦ ¦ ¦ данного селектора. ¦ +-----------------------+---------------------------------------+ ¦ PrestoChangoSelector¦ Генерирует селектор кода, соответству-¦ ¦ ¦ ющий заданному селектору данных, либо¦ ¦ ¦ генерирует селектор данных, соответс-¦ ¦ ¦ твующий селектору кода. ¦ +-----------------------+---------------------------------------+ ¦ SetSelectorBase ¦ Устанавливает базовый адрес селектора.¦ +-----------------------+---------------------------------------+ ¦ SetSelectorLomit ¦ Устанавливает предельное значение се-¦ ¦ ¦ лектора. ¦ L-----------------------+----------------------------------------

Другие подпрограммы API

Администратор этапа выполнения поддерживает следующие допол- нительные подпрограммы API: Прочие подпрограммы API Таблица 17.6 --------------------T------------------------------------------- ¦ Функция ¦ Описание ¦ +-------------------+-------------------------------------------+ ¦ DOS3Call ¦ Вызывает функцию прерывания DOS 21h; вызы-¦ ¦ ¦ вается только из подпрограмм ассемблера. ¦ +-------------------+-------------------------------------------+ ¦ FatalExit ¦ Передает отладчику текущее состояние опе-¦ ¦ ¦ рационной среды защищенного режима и вы-¦ ¦ ¦ выводит подсказку для ввода инструкций о¦ ¦ ¦ продолжении работы. ¦ +-------------------+-------------------------------------------+ ¦ GetDOSEnviroment¦ Определяет текущую строку операционной¦ ¦ ¦ среды задачи. ¦ +-------------------+-------------------------------------------+ ¦ GetVersion ¦ Дает текущую версию операционной среды¦ ¦ ¦ Windows или операционной системы DOS. ¦ +-------------------+-------------------------------------------+ ¦ GetWinFlags ¦ Дает используемые Windows флаги конфигура-¦ ¦ ¦ ции памяти. ¦ +-------------------+-------------------------------------------+ ¦ MessageBox ¦ Создает, выводит на экран и обслуживает¦ ¦ ¦ окно сообщений. ¦ L-------------------+-------------------------------------------- Совместно используемая DLL, чтобы определить, выполняется ли она в защищенном режиме DOS или под Windows, может использовать функцию GetWinFlags, например: if GetWinFlags and wf_DPMI <> 0 then Message('Работа в защищенном режиме DOS') else Message('Работа в среде Windows');

Прямой доступ к DPMI-серверу

Прямой доступ к DPMI-серверу вы можете получить через преры- вание $31, которое непосредственно вызывает DPMI-сервер в обход администратора этапа выполнения. Однако это опасный прием. DPMI не поддерживает очистку ресурсов, таких как векторы прерываний памяти; корректно с этими проблемами работает администратор этапа выполнения. Вы должны глубоко понимать концепции защищенного ре- жима и знать о существенном риске, с которым связано использова- ние данного метода доступа защищенного режима.

Компиляция прикладной программы защищенного режима

В большинстве случаев для получения прикладной программы за- щищенного режима вам не нужно делать ничего особенного. Просто скомпилируйте свою программу, задав в качестве целевой работы за- щищенный режим одним из следующих способов: * В IDE выберите команду Compile¦Target и в диалоговом окне Target Platform (Целевая платформа) выберите Protected-mode Application. * При использовании компилятора, работающего в режиме ко- мандной строки, укажите для выбора в качестве целевой платформы защищенного режима параметр /CP.

Выполнение программы защищенного режима DOS

Когда вы выполняете программу DOS защищенного режима, нужно обеспечить наличие в текущем каталоге или по маршруту DOS файлов DPMI16BI.OVL (сервер DPMI), RTM.EXE (администратор этапа выполне- ния) и всех DLL, с которыми работает ваша программа. Примечание: Лицензионное соглашение позволяет вам распространять файлы DPMI16BI.OVL и RTM.EXE вместе с вашей программой. В выполняемом файле .EXE защищенного режима DOS используется тот же формат файла, что и в Windows 3.x и OS/2 1.x. Этот формат файла является надмножеством обычного формата .EXE DOS и состоит из обычного образа файла .EXE, называемого фиктивным модулем, за которым следует расширенный заголовок и код, данные и ресурсы за- щищенного режима. Ниже показана последовательность событий при выполнении программы защищенного режима DOS. 1. DOS загружает фиктивный модуль реального режима и переда- ет ему управление. 2. Если средства DPMI отсутствуют, то фиктивный модуль заг- ружает DPMI-сервер из файла DPMI16BI.OVL. Некоторые новые администраторы памяти поддерживают средства DPMI (как, например, это делается в окне DOS улучшенного режима Windows 3.х). В таких конфигурациях фиктивный модуль не загружает DPMI-сервер, но использует уже имеющийся. 3. Далее, если администратор этапа выполнения еще не загру- жен в память, фиктивный модуль загружает его из файла RTM.EXE. Если прикладная программа защищенного режима вы- полняет другую программу защищенного режима, обе исполь- зуют одну копию администратора этапа выполнения. 4. Если средства DPMI и администратор этапа выполнения при- сутствуют, фиктивный модуль переключается из реального в защищенный режим и передает управление расширенному заг- рузчику .EXE в администратора этапа выполнения. 5. Загрузчик сначала загружает используемую прикладной прог- раммой DLL (если она имеется), затем загружает сегменты кода и данных прикладной программы. Наконец, загрузчик передает управление на точку входа прикладной программы. При выполнении вашей прикладной программы защищенного режима DOS всегда возможно ситуация, когда уже присутствует DMPI-сервер, отличный от сервера Borland. Поскольку между серверами могут быть небольшие различия, особенно в плане обработки прерываний DOS, вы должны проверить программу и убедиться, что она работает со всеми возможными серверами, которые могут ей встретиться. Когда прикладная программа защищенного режима DOS выполняет- ся в окне DOS улучшенного режима Windows, вы можете управлять объемом расширенной памяти, которую выделяет администратор этапа выполнения, задав в файле .PIF прикладной программы предельное значение памяти XMS.

Управление объемом используемой RTM памяти

По умолчанию администратор этапа выполнения использует при загрузке всю доступную память. Затем по запросам он выделяет па- мять своим клиентам (через подпрограммы API администратора памя- ти). В защищенном режиме нет разницы между обычной памятью (ниже 1 мегабайта) и расширенной памятью (с адресами выше 1 мегабайта); для программ защищенного режима доступны оба типа памяти. Однако администратор этапа выполнение отдает предпочтение расширенной памяти. Только после того как вся расширенная память будет выде- лена, или когда прикладная программа специально запрашивает обычную память (например, с помощью функции GlobalDosAlloc), ад- министратор этапа выполнения выделяет обычную память. Причина, по которой администратор этапа выполнения предпочи- тает расширенную память, заключается в том, что прикладная прог- рамма может с помощью вызова подпрограммы Exec в модуле Dos по- рождать другие прикладные программы. Порожденные прикладные прог- раммы не обязательно являются программами защищенного режима; та- ким образом, им может потребоваться обычная память. Фактически, порожденные программы защищенного режима запускаются как програм- мы реального режима и переключаются в защищенный режим только после успешной загрузки фиктивным модулем средств DPMI и адми- нистратора этапа выполнения. Администратор этапа выполнения перед порождением прикладной программы пытается освободить максимальный объем обычной памяти (например, перенеся перемещаемые блоки в расширенную память). Од- нако попытки освобождения расширенной памяти не предпринимаются. Таким образом, если должны порождаться прикладные программы защи- щенного режима, не использующие администратор этапа выполнения, то необходим споcоб управления распределением памяти администра- тором этапа выполнения. Чтобы управлять тем, сколько памяти может использовать адми- нистратор этапа выполнения, в командной строке DOS добавьте к строке операционной среды DOS переменную среды RTM: SET RTM={параметр nnnn} Возможные параметры перечислены в следующей таблице. Значе- ние nnnn может быть десятичным или шестнадцатиричным числом в ви- де xAB54 или xab54. Параметры переменной операционной среды RTM, используемые для управления памятью Таблица 17.7 ---------------------T------------------------------------------ ¦ Параметр ¦ Описание ¦ +--------------------+------------------------------------------+ ¦ EXTLEAVE nnnn ¦ Всегда оставляет не менее nnnn килобайт¦ ¦ ¦ доступной расширенной памяти. По умолча-¦ ¦ ¦ нию это значение равно 640К. ¦ +--------------------+------------------------------------------+ ¦ EXTMAX nnnn ¦ Не выделяет более nnnn килобайт расширен-¦ ¦ ¦ ной памяти. По умолчанию используется¦ ¦ ¦ значение 4 гигабайта. В Windows использу-¦ ¦ ¦ емое по умолчанию значение равно половине¦ ¦ ¦ доступной памяти. ¦ +--------------------+------------------------------------------+ ¦ EXTMIN nnnn ¦ Если после применения EXTMAX или EXTLEAVE¦ ¦ ¦ доступно менее nnnn килобайт, то програм-¦ ¦ ¦ ма завершается с сообщением о нехватке¦ ¦ ¦ памяти (Out of memory). По умолчанию это¦ ¦ ¦ значение равно 0. ¦ +--------------------+------------------------------------------+ ¦ REALLEAVE nnnn ¦ Всегда оставляет не менее nnnn параграфов¦ ¦ ¦ доступной реальной памяти. По умолчанию¦ ¦ ¦ это значение равно 64К или 4096 парагра-¦ ¦ ¦ фов. ¦ +--------------------+------------------------------------------+ ¦ REALMAX nnnn ¦ Не выделяет более nnnn параграфов реаль-¦ ¦ ¦ ной памяти. По умолчанию это значение¦ ¦ ¦ равно 1 мегабайту или 65535 параграфов. ¦ +--------------------+------------------------------------------+ ¦ REALMIN nnnn ¦ Если после применения REALMAX и REALLEAVE¦ ¦ ¦ доступно менее nnnn параграфов, то прог-¦ ¦ ¦ рамма завершается с сообщением о нехватке¦ ¦ ¦ памяти (Out of memory). По умолчанию это¦ ¦ ¦ значение равно 0. ¦ L--------------------+------------------------------------------- Следующая команда DOS ограничивает RTM 2 мегабайтами расши- ренной памяти и обеспечивает, что нераспределенными останутся 128К реальной памяти. SET RTM=EXTMAX 2048 REALLEAVE 8192
                       Назад | Содержание | Вперед

 

Бесплатный конструктор сайтов и Landing Page

Хостинг с DDoS защитой от 2.5$ + Бесплатный SSL и Домен

SSD VPS в Нидерландах под различные задачи от 2.6$

✅ Дешевый VPS-хостинг на AMD EPYC: 1vCore, 3GB DDR4, 15GB NVMe всего за €3,50!

🔥 Anti-DDoS защита 12 Тбит/с!

VPS в России, Европе и США

Бесплатная поддержка и администрирование

Оплата российскими и международными картами

🔥 VPS до 5.7 ГГц под любые задачи с AntiDDoS в 7 локациях

💸 Гифткод CITFORUM (250р на баланс) и попробуйте уже сейчас!

🛒 Скидка 15% на первый платеж (в течение 24ч)

Новости мира IT:

Архив новостей

IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware

Информация для рекламодателей PR-акции, размещение рекламы — adv@citforum.ru,
тел. +7 495 7861149
Пресс-релизы — pr@citforum.ru
Обратная связь
Информация для авторов
Rambler's Top100 TopList This Web server launched on February 24, 1997
Copyright © 1997-2000 CIT, © 2001-2019 CIT Forum
Внимание! Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав. Подробнее...