Ну, начнем с исторических функций.
Давным-давно, когда даже Билл Гейтс
говорил что 640 килобайт хватит всем, но не у всех были эти 640 килобайт. :) в
биосах существовала функция определения количества базовой памяти.
int 12h
Выходные параметры:
|
Сейчас уже вряд ли кому придет в голову, что базовой памяти может быть меньше
640 килобайт. но мало ли... ;)
Появлялись новые процессоры, и размеры памяти стали расти. в связи с чем
появилась функция определения количества расширенной памяти.
int 15h fn 88h
Входные параметры:
|
Возможно из за архитектуры 286-х процессоров (которым размер шины адреса не
позволяет иметь больше чем 16 мегабайт памяти) эта функция часто имеет
аналогичное ограничение и результат в ax не может превышать 3с00h (Что
составляет 15Мб).
Но, опять таки, появились новые процессоры. 16 мегабайт стало мало.
Вследствие этого появилась еще одна функция BIOS:
int 15h fn e801h
Входные параметры:
|
Не знаю, что означает сконфигурированная память. Так написано в описании.
Здесь производители BIOS видимо оказались неединодушны. Некоторые версии в ax
и bx возвращают 0, это значит что размер памяти следует определять из cx,
dx.
Но видимо и 4 гигабайт оказалось мало. В новых BIOS появилась еще одна функция.
int 15h fn e820h
Входные параметры:
|
Эту функцию нужно вызывать в цикле до тех пор, пока не будет прочитана вся
карта памяти.
Формат структуры таков:
struct { long long base; long long length; long type; };
Поле type может содержать следующие значения:
Функции BIOS не работают в защищенном режиме, поэтому все эти операции необходимо производить еще до перехода в защищенный режим.
Помимо функций BIOS есть еще много других способов.
Самый простой - помереть память самому. :) Делается это из защищенного
режима, страничное преобразование должно быть выключено, адресная линия A20
должна быть включена.
Можно мереть от нуля, но поскольку в первом мегабайте
есть дыры (видеопамять, биосы, просто дыры), удобнее делать это начиная с
первого мегабайта.
Вовсе не обязательно проверять каждый байт, достаточно проверять один байт на
какое-то определенное количество памяти. Определенным количеством памяти можно
посчитать мегабайт, но лучше (хотя и медленнее) за единицу памяти принять одну
страницу памяти (4к).
Во избежание неприятностей память лучше не разрушать, а восстанавливать в
первоначальном виде. делается это примерно так:
xchg [ebx], eax
xchg [ebx], eax
Если после этого в eax содержится то же значение, которое было до того,
значит память присутствует по данному адресу. Если возвратилось 0ffffffffh,
значит память отсутствует, если же что ни будь другое - то это может быть ROM,
хотя после мегабайта вы вряд ли встретите какой либо BIOS. В любом случае если
память по текущему адресу не обнаружена, значит, память закончилась и дальше
искать чревато... существуют еще различные типы памяти (ACPI например) которую
не стоит трогать.
Из защищенного режима можно воспользоваться содержимым CMOS, некоторые ячейки
в нем BIOS заполняет определенными при начальном тесте системы значениями. Но
здесь все не так однозначно как хотелось бы. Разные версии BIOS могут хранить
значения в разных местах.
Байты 30-31 принято считать стандартными, но они определяют только 64Мб
памяти. Не очень то подходят для использования.
Почти любое приложение пользуется динамически выделяемыми блоками памяти
(известная, наверное, всем функция malloc в c). Сейчас мы поговорим о том, как
это все работает.
Подходить к этому можно по разному, но принцип везде прослеживается один. На
каждый блок памяти необходимо иметь структуру, описывающую занятось блока, его
размер. В примитивной реализации это может выглядеть так, как это сделано в
DOS.
В ДОСе вся память на равных правах принадлежит всем запущенным программам. Но
чтобы операционная система могла как-то контролировать использование памяти, в
ДОСе применяются MCB (Memory Control Block). Формат этого блока таков:
struct { char Signature; unsigned short OwnerId; unsigned short SizeParas; char Reserved[3]; char OwnerName[8]; };
Размер структуры 16 байт (1 параграф памяти) и эта структура непосредственно
предшествует описываемому блоку памяти.
Размер блока указывается в параграфах
в поле SizeParas. Такая структура вполне подходит для ограниченной по размерам
памяти DOS, но для приложений она не очень то применима. Разница состоит в том,
что в случае ДОС, чтобы найти блок свободной памяти (Такие блоки помечаются
нулевым OwnerId), необходимо пройти по всем блокам от начала цепочки, до тех
пор, пока не встретится свободный блок соответствующего размера. В ДОСе имеется
функция, с помощью которой можно получить адрес первого блока (Base MCB) (int
21h, fn 52h).
Столь медленный поиск не страшен для DOS, у которого количество
блоков редко превышает несколько десятков, но в приложениях поиск по цепочке
блоков может быть достаточно долгой процедурой.
Поэтому в приложениях обычно
применяется другой алгоритм, который заключается в следующем. (Я рассмотрю
наиболее быстрый алгоритм, вариантов, конечно, может быть множество):
У каждого блока, как я уже говорил, есть два основных параметра: размер и
флаг занятости. Оба эти параметра размещаются в одном двойном слове памяти.
Поскольку как начало блока, так и его размер обычно выравниваются на четное
число байт, младшие биты размера остаются неиспользуемыми (всегда равны нулю) и
флаг занятости размещается в одном из них.
Этот параметр блока размещается перед началом и по окончанию блока. Начальный
параметр следующего блока соответственно будет размещен непосредственно после
конечного параметра предыдущего, что позволит анализировать цепочку блоков с
одинаковым успехом в обоих направлениях.
Свободные блоки памяти размещаются в списках в соответствии со своим
размером. Размер блоков в списках увеличивается в геометрической прогрессии. К
примеру, в первом списке хранятся блоки до 16 байт длиной, во втором до 32-х
байт длиной и так далее. Такая система позволяет, зная размер необходимого
блока, сразу же выбирать из соответствующего списка подходящий блок и не требует
поиска по всем блокам. Для организации списков к блоку добавляются несколько
параметров (поскольку блок свободен, и его внутреннее пространство может быть
использовано для любых целей, эти параметры размещаются в самом блоке). К этим
параметрам относятся ссылка на следующий свободный блок в списке, и номер списка
в котором находится блок. (Это позволяет ускорить удаление блока из списка).
Для выделения блока необходимого размера сперва проверяется список
соответствующего размера, в котором может потребоваться поиск блока. Если
соответствующий список пуст, то проверяется следующий список, в котором уже не
требуется проводить поиска, поскольку любой блок заведомо больше нужного
размера. Найденный пустой блок делится на две части, вторая - не нужная часть
оформляется как свободная и помещается в соответствующий список, а первая часть
оформляется как занятая и возвращается программе.
Из-за необходимости введения дополнительных параметров для свободных блоков
памяти минимальный размер блока не может быть меньше 8 байт. Даже если
пользователь захочет получить блок меньшего размера, выделится блок в 8 байт
длиной.
При освобождении блока, если предыдущий или последующий блоки пусты, он
объединяется с ними в один блок и добавляется в список соответствующего размера.
Использованные окружающие блоки удаляются из тех списков, в которых они были
записаны ранее.
Для того, чтобы предотвратить попытку объединения первого блока памяти (при
его освобождении) с предшествующим ему, перед первым блоком ставится параметр с
флагом занятости. То же самое делается и для последнего блока памяти, но только
после него.
Не буду пока вдаваться в тонкости реализации всего этого, если вас заинтересовало, то в ближайших выпусках рассмотрим. А этот выпуск заканчиваю. Жду от вас с нетерпением отзывов, пожеланий. До скорых встреч.
Отправлено 2002-02-15 для 6542 подписчиков.
ведущий рассылки Dron
Сайт проекта
Архив
Рассылки
При поддержке Kalashnikoff.ru