Типы сегментов
В операционной системе MS-DOS с точки зрения процессора все сегменты памяти были одинаковыми. Деление их на сегменты кода и сегменты данных достаточно условное, так как в MS-DOS нет никаких препятствий для того чтобы загрузить в CS:IP адрес любого сегмента, например, сегмента данных программы. Точно также программа могла выполнять любые операции в своем (или чужом) сегменте кода, или в сегменте, который принадлежит операционной системе. Так как в MS-DOS одновременно может работать только одна программа, вся оперативная память отдается ей в полное распоряжение.
В мультизадачной операционной системе Windows память используется одновременно многими приложениями. Схема адресации защищенного режима эффективно защищает ядро Windows от приложений и позволяет в расширенном режиме увеличить размер глобальной области памяти за счет механизма виртуальной памяти.
Однако для приложений нужны блоки памяти, занимающие непрерывное адресное пространство. Если в мультизадачной операционной системе не принимать никаких мер для уменьшения фрагментации адресного пространства, при интенсивной работе приложений очень скоро приложения не смогут получить непрерывный блок памяти даже при достаточном общем объеме свободной памяти.
К счастью, операционная система Windows умеет объединять свободные блоки памяти, используя механизм перемещаемых (moveable ) и удаляемых (discardable ) сегментов.
Операционная система Windows версии 3.0 могла работать как в реальном, так и в защищенном режиме. Перемещение блоков памяти в реальном режиме работы процессора представляет собой нетривиальную задачу, так как необходимо обеспечить работу приложения с блоками памяти, логический адрес которых может произвольно изменяться операционной системой в процессе перемещения. Эта проблема решалась с помощью двухступенчатой схемы получения доступа к блоку памяти.
Согласно этой схеме на первом шаге получения доступа приложение заказывает для себя блок памяти, расположенный в фиксированном или перемещаемом сегменте.
Фиксированный (fixed ) сегмент имеет постоянный логический адрес <селектор:смещение> и не никогда не перемещается в адресном пространстве. Если ваше приложение заказывает для себя память при помощи функции malloc , она получает память в фиксированном сегменте.
Блок памяти, расположенный в перемещаемом сегменте, не имеет постоянного логического адреса. При создании этот блок получает идентификатор, который и используется для ссылки. Windows может в любой момент времени переместить его в любое место адресного пространства для объединения свободных блоков памяти.
Вы можете спросить, а как же пользоваться перемещаемым блоком памяти, не имеющим постоянного адреса?
Очень просто, на втором шаге непосредственно перед использованием перемещаемый блок памяти необходимо зафиксировать, вызвав специальную функцию из программного интерфейса Windows.
После использования блок памяти следует расфиксировать, для того чтобы Windows могла его перемещать.
В защищенном режиме возможно перемещение зафиксированных блоков памяти, при этом их логический адрес не изменяется. Вместо этого изменяется базовый адрес в локальной таблице дескрипторов. Поэтому перемещение зафиксированных блоков памяти происходит для приложений незаметно. Это обстоятельство значительно упрощает приемы работы с сегментами.
Таким образом, даже если ваше приложение, работающее в защищенном режиме, заказало фиксированный блок памяти, он все равно остается перемещаемым! Поэтому, создавая приложения для Windows версии 3.1 вы можете использовать фиксированные блоки памяти почти без ущерба для эффективности работы системы дефрагментации (так как логический адрес фиксированного блока не изменяется, это может привести к невозможности освобождения непрерывного пространства в таблице дескрипторов, нужного для адресации блоков памяти размером, большим 64 Кбайт).
Если же вам и в самом деле необходимо получить блок памяти с фиксированным линейным адресом, придется вызывать специальную функцию GlobalFix , запрещающую изменение базового адреса в локальной таблице дескрипторов.
Кроме того, если нужно обеспечить постоянное присутствие данного сегмента в физической памяти, для такого блока следует отключить механизм страничного обмена, вызвав функцию GlobalPageLock .
Фиксирование линейного адреса и отмена страничного обмена - крайняя мера. Обычные приложения Windows не должны вызывать функции GlobalFix и GlobalPageLock, так как это может привести к снижению производительности работы операционной системы и приложений.
Логический адрес перемещаемого блока памяти, состоящий из селектора и смещения, может произвольно изменяться операционной системой Windows. Для фиксирования блоков памяти в логическом адресном пространстве необходимо использовать функции GlobalLock или LocalLock.
Операционная система Windows версии 3.1 перемещает зафиксированные блоки памяти, изменяя базовый адрес в локальной таблице дескрипторов, поэтому такое перемещение не приводит к изменению логического адреса <селектор:смещение> и незаметно для приложений. Даже если приложение заказывает фиксированный блок памяти, но не вызывает функцию GlobalFix, этот блок памяти будет перемещаемым.
Другой тип сегментов, предусмотренный в Windows, - удаляемые сегменты. В любой момент времени Windows может удалить сегмент из памяти, сохранив, однако, его идентификатор. Память, занимаемая ранее удаляемым сегментом, может быть использована Windows для других приложений.
При попытке зафиксировать удаленный сегмент приложение получает код ошибки. В этом случае приложение должно самостоятельно восстановить содержимое сегмента и затем продолжить свою работу.
В файле определения модуля вы можете указать, что сегмент кода вашего приложения должен быть перемещаемым и удаляемым, а сегмент данных - перемещаемым. Для этого используются параметры moveable и discardable:
CODE preload moveable discardable DATA preload moveable multiple
В этом случае операционная система может перемещать память приложения и при необходимости удалять сегмент кода неактивного приложения из памяти. Если этот сегмент потребуется вновь, Windows самостоятельно загрузит его из exe-файла приложения.