[ Assembler & Win32. Курс молодого бойца. Урок 2 ]
Память системы
Прежде чем рассказывать, о языке ассемблера я расскажу вам, как устроена память
на Win32 системах. Программировать на ассемблере не зная, как устроена память
системы бессмысленно. Самое главное преимущество ассемблера состоит во взаимодействии
с памятью.
Ладно, приступим. Сначала я объясню, что такое смещение. Смещение это положение
относительно чего-то. Смещение может быть в файле, в памяти и вообще во всём. Если
говорить по-другому, то смещение это положение, адрес, местонахождение всегда измеряется в
байтах. У каждой переменной есть смещение, у каждой команды есть смещение.
Немного истории.
Теперь о памяти. В ДОС и в windows 3.Х память была сегментной. Вся память делилась на
сегменты по 64 КВ., и адрес (именно адрес, а не смещение) задавалась, форматом сегмент: офсет.
Сегмент задаёт порядковый номер сегмента, а офсет задаёт смещение чего-либо в этом сегменте.
Память
Сегмент 1 (64kb) Сегмент 2 (64kb) Сегмент 3 (64kb) Сегмент 4 (64kb) и так далее
Учтите, что это объяснение для 16-разрядных программ, о 32-разрядных я расскажу позже,
(но не пропускайте эту часть, важно понять, что такое 32-разрядность).
Таблица выше иллюстрирует общую память, разделенную на сегменты по 64kb. Здесь максимум
65536 сегментов. Теперь возьмите один из сегментов:
Сегмент 1(64kb)
Смещение 1 Смещение 2 Смещение 3 Смещение 4 Смещение 5 и так далее
Вы спросите, зачем нужна эта сегментность. Всему есть разумное объяснение. Дело в том в
том, что ДОС 16 битный. И регистры 16 битные. С помощью 16 бит можно адресовать только 65536
байт памяти и 65536 сегментов.
Например: 0030:4012 (всё шестнадцатиричное)
Это означает: сегмент 30, смещение 4012. Чтобы узнать, что находится в том адресе, вы сначала
переходите на сегмент 30, а затем в сегменте смещаетесь на 4012. В предыдущих уроках
мы узнали о сегментных и указательных регистрах. Например, сегментные регистры:
* CS - Сегмент кода
* DS - Сегмент данных
* SS - Сегмент стека
* ES - Дополнительный сегмент
* FS - Универсальный сегмент
* GS - Универсальный сегмент
Названия говорят о их функциях: сегмент кода (CS) содержит номер секции, где вылнен
текущий код. Сегмент данных для получения данных из текущего сегмента. На стек указывает
сегмент стека (SS) (подробнее о стеке я раскажу позже), ES, FS, GS - универсальные регистры
и могут использоваться для любого сегмента (не в win32).
Теперь о Win32.
В 16-разрядном программировании, сегменты необходимы. К счастью, эта проблема решена в
32-разрядном Windows (95 и выше). Вы все еще имеете сегменты, но вам не нужно заботиться о
них, потому, что они уже не 64kb (как в 16-разрядном), а 4 Гб. Windows вероятно даже "повиснет",
если вы попытаетесь изменить один из сегментных регистров. Это называеся плоской моделью памяти
(flat). Здесь есть только смещения и они теперь 32-разрядные (в диапазоне от 0 до 4,294,967,295).
Каждая ячейка в памяти указывается смещением. Это действительно одно из лучших преимуществ
32-разрядного программирования над 16-разрядным. Так что теперь вы можете забыть о сегментных
регистрах и сосредоточиться на других регистрах.
Вот общая схема памяти в Win32. (1.gif)
Но 4ГБ может и не быть на машине, поэтому эта память называется виртуальной. Мало того,
каждый процесс выполняется в своём виртуальном адресном пространстве. А это значит, что
никакой другой процесс не сможет получить доступ к вашей памяти. Для этого существуют
специальные API функции, но для их использования нужно иметь специальные привилегии в системе.
Память 0-0000FFFF не используется и служит для выявления нулевых указателей, значит, если вы
укажете адрес 0000С567, то он будет считаться нулевым, удобно, не правда ли? Любая попытка
обратится к этой памяти приводит к ошибке. Память выше 80000000 одна для всех процессов. В
этой памяти находится код нулевого кольца, структуры ядра, код планировщика задач, код драйверов,
диспетчер ввода вывода, таблица прерываний и т.д. Любая попытка обратиться к памяти ядра
приводит к ошибке и к немедленному завершению приложения. Память в диапазоне 00001000-7FFFFFFF
доступна для 3 кольца, т.е. для вашего приложения. С ней вы, что хотите то и делаете, в неё
также грузятся Дллки.
Выделение памяти.
Если вам даётся 2ГБ памяти, то это не означает, что вы можете обратиться к любому участку памяти.
Для того чтобы получить доступ к некоторому участку памяти надо сначала её зарезервировать.
Грубо говоря, перед резервированием памяти, резервируемого участка памяти просто не существует
(я не оговорился её просто не существует), вы как бы его создаёте и задаёте ему атрибуты доступа
(полный доступ, только чтение, только запись, нет доступа). Минимальный размер выделяемой
памяти - страница, равна 1000 байтам (для тех, кто не привык к шестнадцатеричной записи,
это 4096 байт - 4КБ). Даже если вы захотите выделить 5 байт, то всё равно выделится 4КБ.
Такой метод распределения памяти называется гранулярность. Думаю, вы не столкнётесь с тем,
что вам надо будет выделять память, лично мне это ни разу не пригодилось. Это нужно при работе
с файлами для того, что бы в эту память читать файл.
Не расслабляемся, читаем следующий, урок.
Фотки к статье ищи в /includes/asm/*
(с) Руслан Аблязов