Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

Операционная система "с нуля" на Ассемблере и Cи (Выпуск №12)


Служба Рассылок Subscribe.Ru

Здравствуйте, уважаемые подписчики.



Извиняюсь за долгое отсутствие выпусков. На это есть достаточно веские причины (у меня родился сын :). Но время идет, и пора уже что нибудь написать, дабы люди не подумали что рассылка умерла. Нет, рассылка просто спит.



Выпуск №12


Новости.



В этом выпуске:


  • Определение количества памяти.
  • Динамическое распределение памяти в процессах.


Определение количества памяти.



Определение количества памяти через BIOS.


Ну, начнем с исторических функций.
Давным-давно, когда даже Билл Гейтс говорил что 640 килобайт хватит всем, но не у всех были эти 640 килобайт. :) в биосах существовала функция определения количества базовой памяти.

int 12h

Выходные параметры:

  • ax - размер базовой памяти в килобайтах.

Сейчас уже вряд ли кому придет в голову, что базовой памяти может быть меньше 640 килобайт. но мало ли... ;)

Появлялись новые процессоры, и размеры памяти стали расти. в связи с чем появилась функция определения количества расширенной памяти.

int 15h fn 88h

Входные параметры:

  • ah = 88h
Выходные параметры:
  • ax - размер расширенной памяти в килобайтах.

Возможно из за архитектуры 286-х процессоров (которым размер шины адреса не позволяет иметь больше чем 16 мегабайт памяти) эта функция часто имеет аналогичное ограничение и результат в ax не может превышать 3с00h (Что составляет 15Мб).

Но, опять таки, появились новые процессоры. 16 мегабайт стало мало. Вследствие этого появилась еще одна функция BIOS:

int 15h fn e801h

Входные параметры:

  • ax = e801h.
Выходные параметры:
  • ax - размер расширенной памяти в килобайтах до 16Mb;
  • bx - размер расширенной памяти в блоках по 64к свыше 16Мб;
  • cx - размер сконфигурированный расширенной памяти в килобайтах до 16Mb;
  • dx - размер сконфигурированной расширенной памяти в блоках по 64к свыше 16Мб.

Не знаю, что означает сконфигурированная память. Так написано в описании.

Здесь производители BIOS видимо оказались неединодушны. Некоторые версии в ax и bx возвращают 0, это значит что размер памяти следует определять из cx, dx.

Но видимо и 4 гигабайт оказалось мало. В новых BIOS появилась еще одна функция.

int 15h fn e820h

Входные параметры:

  • eax = e820h;
  • edx = 534d4150h ('SMAP');
  • ebx - смещение от начала карты памяти;
  • eсx - Размер буфера;
  • es:di - Адрес буфера для размещения карты памяти.
Выходные параметры:
  • eax - 534d4150h ('SMAP');
  • ebx - следующее смещение от начала карты памяти, если = 0, то вся карта передана;
  • ecx - Количество возвращенных байт;
  • буфер заполнен информацией;

Эту функцию нужно вызывать в цикле до тех пор, пока не будет прочитана вся карта памяти.

Формат структуры таков:

struct {
  long long base;
  long long length;
  long      type;
};

Поле type может содержать следующие значения:

  • 1 - Доступно для использования операционной системой;
  • 2 - Зарезервировано (например, ROM);
  • 3 - ACPI reclaim memory (Доступно для операционной системы после прочтения таблицы ACPI;
  • 4 - ACPI NVS memory (Операционной системе требуется сохранять эту память между NVS сессиями).
Проверить как работает эта функция у меня не получилось, мой BIOS ее не поддерживает. :(
Но в заключение скажу следующее. Все функции в случае ошибки (если функция не поддерживается) возвращают установленный флаг cf. В случае отсутствия новых функций необходимо обращаться к более старым.

Функции BIOS не работают в защищенном режиме, поэтому все эти операции необходимо производить еще до перехода в защищенный режим.


Определение размера памяти другими способами:


Помимо функций BIOS есть еще много других способов.

Самый простой - помереть память самому. :) Делается это из защищенного режима, страничное преобразование должно быть выключено, адресная линия A20 должна быть включена.
Можно мереть от нуля, но поскольку в первом мегабайте есть дыры (видеопамять, биосы, просто дыры), удобнее делать это начиная с первого мегабайта.

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

Во избежание неприятностей память лучше не разрушать, а восстанавливать в первоначальном виде. делается это примерно так:

xchg [ebx], eax
xchg [ebx], eax

Если после этого в eax содержится то же значение, которое было до того, значит память присутствует по данному адресу. Если возвратилось 0ffffffffh, значит память отсутствует, если же что ни будь другое - то это может быть ROM, хотя после мегабайта вы вряд ли встретите какой либо BIOS. В любом случае если память по текущему адресу не обнаружена, значит, память закончилась и дальше искать чревато... существуют еще различные типы памяти (ACPI например) которую не стоит трогать.

Из защищенного режима можно воспользоваться содержимым CMOS, некоторые ячейки в нем BIOS заполняет определенными при начальном тесте системы значениями. Но здесь все не так однозначно как хотелось бы. Разные версии BIOS могут хранить значения в разных местах.

  • 15h - Базовая память в килобайтах (младший байт) (IBM);
  • 16h - Базовая память в килобайтах (старший байт) (IBM);
  • 17h - Расширенная память в килобайтах (младший байт) (IBM);
  • 18h - Расширенная память в килобайтах (старший байт) (IBM);
  • 30h - Расширенная память в килобайтах (младший байт) (IBM);
  • 31h - Расширенная память в килобайтах (старший байт) (IBM);
  • 34h - Расширенная память более 16Мб (блоками по 64к) (младший байт) (AMI);
  • 35h - Расширенная память более 16Мб (блоками по 64к) (старший байт) (AMI);
  • 35h - Расширенная память (блоками по 64к) (младший байт) (AMI WinBIOS);
  • 36h - Расширенная память (блоками по 64к) (старший байт) (AMI WinBIOS);

Байты 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


Подписаться на рассылки

Рассылки Subscribe.Ru
Операционная система "с нуля" на Ассемблере и Cи
Ассемблер? Это просто! Учимся программировать
Ассемблер? Это просто! Учимся программировать (FAQ)
(C)Москва, 2001. Авторское право принадлежит Валяеву А.Ю. Публичное размещение материала из рассылки, а также его использование полностью или частично в коммерческих или иных подобных целях без письменного согласия автора влечет ответственность за нарушение авторских прав.


http://subscribe.ru/
E-mail: ask@subscribe.ru
Отписаться
Убрать рекламу

В избранное