среда, 7 октября 2009 г.

История GlobalLock, часть 1: ранние годы

Это перевод A history of GlobalLock, part 1: The early years. Автор: Реймонд Чен.

Когда-то, давным-давно, была Windows 1.0. Это были действительно доисторические времена. 640 Кб. Сегменты. Ближние и дальние указатели. Нет виртуальной памяти. Кооперативная многозадачность.

Поскольку нет виртуальной памяти, то выгрузка памяти на диск должна производиться с поддержкой приложения. Когда производилась попытка выделить память (для кода или для данных) и не было достаточно свободной памяти, то менеджер памяти должен был выполнить процесс под названием "упаковка" ("compaction"), чтобы освободить непрерывный блок свободной памяти.
  • Сегменты кода могли быть отброшены (discard) свободно, поскольку они в любой момент могли быть загружены из оригинального EXE файла (без виртуальной памяти - нет никаких вещей типа "paged out"). Отбрасывание сегментов кода требовало дополнительной работы, чтобы быть уверенными в том, что когда в следующий раз код будет вызван, то он будет загружен в память снова. Как это делалось, нам сейчас не столь важно, хотя это было довольно сложный и занятный процесс.
  • Память, содержащая код, могла быть перемещена в другое место, а все ссылки в старых адресах менялись, чтобы соответствовать новому размещению. Это также было непростым делом, но нам сейчас это не интересно.
  • Память, содержащая данные, могла быть перемещена, но ссылки на старые адреса не менялись. Защищать свои данные от перемещения во время работы было задачей самих приложений.
  • Память, которая была заблокирована (locked) или фиксированной (fixed) (или третьей категории, "wired" - но давайте не будем в это залезать) никогда не перемещалась.
Когда вы выделяли память через GlobalAlloc, вам сначала нужно было решить, хотите ли вы "перемещаемую" ("moveable") память (память, которую может тасовать менеджер памяти) или же "фиксированную" ("fixed") память (память, которая имела иммунитет к перемещениям). Соответственно, блок "фиксированной" памяти был как бы "перемещаемым" блоком, который заблокировали навечно.

Приложениям настоятельно не рекомендовалось выделять "фиксированную" память, потому что она тормозила менеджер памяти (думайте об этом, как аналоге неперемещаемого блока на диске для дефрагментатора).

Возвращаемое значение GlobalAlloc было описателем (handle) глобального блока памяти или HGLOBAL. Это значение само по себе было бесполезно. Вам нужно было вызвать GlobalLock, чтобы сконвертировать этот HGLOBAL в указатель, который вы могли бы использовать.

GlobalLock делала несколько вещей:
  • Она явно подгружала память (если она была сброшена). Возможно при этом выгружались или перемещались другие блоки памяти, чтобы освободить место для блокируемого блока памяти.
  • Если блок памяти был "перемещаемым", то функция также увеличивала его "число блокировок" ("lock count"), запрещая, таким образом, менеджеру памяти двигать блок во время операций упаковки памяти ("число блокировок" для "фиксированной" памяти было не нужным, поскольку "фиксированные" блоки памяти никогда не перемещаются).
Приложениям настоятельно рекомендовалось держать глобальные блоки памяти заблокированными как можно меньше, чтобы избегать фрагментации кучи. Указатели на разблокированную "перемещаемую" память были запрещены, поскольку даже малейший вздох (типа вызова функции, которая была раньше выгружена) мог запустить процесс упаковки и сделать указатель негодным.

Okей, как всё это взаимодействовало с GlobalReAlloc?

Это зависело от того, как была выделена памяти и каково было её состоянии блокировки.

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

Если память была выделена как "перемещаемая", но была заблокирована, или же она была "фиксированной", то менеджер памяти мог изменять её размер только на месте, без перемещения. Он не мог перемещать память потому что (в случае заблокированной "перемещаемой" памяти) всё ещё существовали действующие указатели на неё, что было указано ненулевым счётчиком блокировок, или (в случае "фиксированной" памяти) фиксированная память была выделена в расчёте на то, что она никогда не будет перемещаться.

Если память была выделена как "перемещаемая" и была заблокирована (или она была "фиксированной"), то вы могли передавать флаг GMEM_MOVEABLE, чтобы аннулировать поведение "можно только изменять размер на месте". В этом случае менеджер памяти мог двигать память, если это было необходимо. Передача флага GMEM_MOVEABLE означала: "нет, правда, я как бы в курсе, что по правилам, ты не можешь этого делать, но я тебя прошу это сделать. Я честно обещаю взять на себя всю ответственность и обновить все мои указатели, которые ссылаются на эту память, чтобы они указывали на новое место".

(Реймонд действительно помнит работу в Windows 1.0. К счастью, очень помогли терапевтические сессии).

В следующий раз: пришествие селекторов.

Комментариев нет:

Отправить комментарий

Можно использовать некоторые HTML-теги, например:

<b>Жирный</b>
<i>Курсив</i>
<a href="http://www.example.com/">Ссылка</a>

Вам необязательно регистрироваться для комментирования - для этого просто выберите из списка "Анонимный" (для анонимного комментария) или "Имя/URL" (для указания вашего имени и ссылки на сайт). Все прочие варианты потребуют от вас входа в вашу учётку.

Пожалуйста, по возможности используйте "Имя/URL" вместо "Анонимный". URL можно просто не указывать.

Ваше сообщение может быть помечено как спам спам-фильтром - не волнуйтесь, оно появится после проверки администратором.

Примечание. Отправлять комментарии могут только участники этого блога.