Всем привет!
Часть 1. Общие сведения
1.1. Процессор и его регистры.
В компьютерной системе всё вращается вокруг центрального процессора CPU. Он работает под управлением какой-либо системы. Программист пишет инструкций, которые система помещает в память ОЗУ. В свою очередь процессор берёт инструкции из памяти в свои регистры, выполняет их, и помещает опять в память. Цикл повторяется до тех пор, пока пользователь не остановит программу.
CPU имеет огромное кол-во регистров, и только малая их часть предназначена непосредственно для выполнения кода – эти регистры назвали РОН, или регистры общего назначения:
Но такая картина только снаружи процессора. Попав в контейнер процессорного ядра, все регистры переименовываются по типу
Кроме РОН, процессор имеет и следующие регистры:
1.2. Формат и набор команд процессора х86.
Для начала, рассмотрим такой ассемблерный код в виндовом отладчике ‘DEBUG’..
Здесь командой
1.3. Оперативная память ОЗУ.
Как упоминалось выше, для быстрого доступа к инструкциям кода, система считывает программу с жёсткого диска, в память ОЗУ. При этом, чтобы не забивать напрасно память, программа читается с диска не целиком, а кусками по 4 Кб – т.н. страницами. Когда процессор выполнит все инструкции этой страницы, процессор сгенерит исключение
На такой подкачке страниц с диска, основана виртуальная память защищённого режима. Например, можно по-очереди отображать в одну 4-Кбайтную страницу физической памяти, хоть 1000 страниц с диска и создастся иллюзия, как-будто на машине установлено 8-16 Gb физ.памяти. Правда работать такая программа будет как черепаха, т.к. всё время будет уходить на замещение страниц.
Процессор соединяется с памятью 64-битной шиной, однако выбор ячейки на модуле памяти требует двумерного адреса, т.е. адреса-строки, и адреса-столбца. Такой адрес назвали DRAM-адресом. Преобразованием адресов занимается контроллёр-памяти. Он разбивает линейный адрес процессора на две составляющие, и засылает их логике DDR по очереди – сначала строку, потом столбец. Чтобы логика модуля поняла где-какой адрес, контроллёр подтверждает их стробами #RAS (row-address-strobe) и #CAS (column, столбец) соответственно. Время между этими стробами (задержка) входит в состав ‘таймингов памяти’ и известно под названием RAS-to-CAS Delay.
В плане расхода тактов, обмен с динамической памятью DDR для процессора операция слишком медленная. Поэтому, чтобы не обращаться к ней часто, проц за один раз загребает сразу по 64-байта данных, которые помещает в свой кэш. Такой обмен назвали пакетным, а длину пакета задаёт параметр
Другими словами, при любых обстоятельствах (хоть запись, хоть чтение) процессор обменивается с памятью только 64-байтными кэш строками ‘Cache-Line’ в независимости от того, нужен ему байт или двойное слово. Он никогда не станет запрашивать шину ради одного байта, ведь шина – это общий ресурс, который может быть занят, например если кто-то в данный момент использует прямой обмен с памятью посредством DMA. Информацию о размере своей кэш-линейки можете почерпнуть из программы CPU-Z.
Основной проблемой DDR-SDRAM является время доступа к ней. Связано это с тем, что запоминающая матрица её ядра собрана на крошечных конденсаторах, время разряда которых физически не способно превысить частоту в 200 МГц. На более высоких частотах, кондёры просто не успевают полностью разряжаться, что приводит к искажению данных.
Но DRAM дешёвая и отказываться от неё пока рановато. Поэтому инженеры пошли другим путём, и ввели понятие 2n-prefetch (предвыборка). Суть фишки - в увеличении числа линий-чтения с матрицы. N считается тут степенью двойки. Например, для DDR2 n=2, или 2^2=4 параллельные линии, для DDR3 получаем уже 2^3=8 линий, для DDR4 соотвественно 16. Предвыбранные (prefetch) данные накапливаются в промежуточном буфере, который через мультиплексор MUX может сливать информацию с ядра, на любой частоте. Таким образом удалось решить проблему века, придерживаясь опорной частоты запоминающей матрицы в 200 МГц:
Вообще идея использовать конденсаторы для хранения битов не совсем удачна, т.к. они имеют свойства со-временем разряжаться. Значит нужно периодически считывать с них заряд, и записывать его обратно. Этот процесс известен как ‘регенерация ячеек памяти’. До модулей DDR2 регенерацией строк занимался контроллёр-памяти, а начиная с DDR3 - функцию перенесли внутрь DRAM-чипов. Теперь проблема ушла на второй план и выполняется в фоне. Конроллёр просто выделяет чипам определённое время на регенерацию, которое (среди таймингов) числится в розысках под кличкой ‘tRP’:
1.4. Кэш-память процессора.
Другое дело кэш процессора, где в роли запоминающих элементов выступают не конденсаторы, а пара транзисторов, включённых по схеме триггера. Такой тип памяти получил название ‘статическая’ или SRAM. Тут не нужно регенерировать ячейки, на то она и статическая. Процессор получает доступ к ней мгновенно, т.к. его регистры собраны на таких-же триггерах. Ему без разницы от куда брать данные, с другого регистра или со-своего кэш – скорость практически одинакова, если не учитывать небольшие издержки на внутреннюю шину.
Процессор имеет трёхуровневую организацию кеш: L1, L2 и L3. Первые два уровня физически разделены пополам – половина для кода, половина - для данных. Кэш L3 является общим и имеет наибольшую ёмкость памяти. Внутри своего кэш, процессор хранит только действительно нужную информацию и ту, что может потребоваться в будущем (предварительная загрузка данных). Именно поэтому он читает из памяти не единичными байтами, а прихватывает с собой сразу и последующие 63. Практика доказала, что программисты пишут код последовательно и лишь иногда появляются циклы. Значит кэш-контроллёр действительно знает своё дело, раз читает инфу пачками.
Структура и работа кэш памяти - довольная обширная тема, которая потянет на отдельную статью. Только кэш поддерживается полностью на аппаратном уровне, и асм-программисту доступны лишь некоторые средства работы с ним - например полная его очистка, когда хотим получить время выполнения (профайл) определённого участка кода. Поэтому мы не будем копать в эту тему глубоко, а позже - двинемся дальше..
Поскольку зарегистрировался на данном форуме, внесу и я свои 5-копеек в общую копилку статей. Занимаюсь по-большей частью ассемблером, то и статья на эту тему. Гуру в этой области прошу строго не судить, т.к. с изложением своих мыслей всегда имел проблемы. Поскольку пишу по-ходу дела и в свободное время, то между частями могут быть перерывы. Рассматривается работа процессоров x86 в двух режимах – реальном RM, и зашищённом РМ. Как и принято - начну издалека..
Часть 1. Общие сведения
1.1. Процессор и его регистры.
В компьютерной системе всё вращается вокруг центрального процессора CPU. Он работает под управлением какой-либо системы. Программист пишет инструкций, которые система помещает в память ОЗУ. В свою очередь процессор берёт инструкции из памяти в свои регистры, выполняет их, и помещает опять в память. Цикл повторяется до тех пор, пока пользователь не остановит программу.
CPU имеет огромное кол-во регистров, и только малая их часть предназначена непосредственно для выполнения кода – эти регистры назвали РОН, или регистры общего назначения:
EAX, EBX, ECX, EDX
и т.д. Гугл не напрягаясь выдаст кучу ссылок на их описание.Но такая картина только снаружи процессора. Попав в контейнер процессорного ядра, все регистры переименовываются по типу
R0, R1..RN,
где N – нагрузка на процессор. Блок переименования ‘Rename’ ввели для параллельного исполнения инструкций на одном ядре процессора, и теперь не возникает путаницы, какому именно потоку принадлежит та или иная инструкция. На выходе из ядра, регистры опять приобретают свои имена EAX
и прочие.Кроме РОН, процессор имеет и следующие регистры:
- Пять регистров-управления самим процессором
CR0..CR4
(Control Registers); - Четыре регистра управления памятью
GDTR, LDTR, IDTR, TR
(Descriptor Table Registers); - Восемь регистров-отладки
DR0..DR7
(Debug Registers); - Более 1000 специфических регистров
MSR
(Model-Specific Registers).
1.2. Формат и набор команд процессора х86.
Для начала, рассмотрим такой ассемблерный код в виндовом отладчике ‘DEBUG’..
Здесь командой
а
я ассемблировал некоторый код, который потом командой u
дизассемблировал:- Всё-что выделено голубым – это команды процессора. Общее их кол-во давно перевалило за 200, и в каждом процессоре появляются всё новые и новые. Забегая вперёд скажу, что львиная их доля никому не нужна, кроме самих AMD и INTEL - мы будем использовать из них всего штук 20-30.
- Выделенное красным – это операнды команд. Слева от запятой находится приёмник, а справа – источник, который может быть регистром, константой, или значением в памяти. Если операнд является значением в памяти, то он берётся в квадратные скобки (по крайней мере в fasm’e, самом ‘правильном’ ассемблере из всех). Здесь в ход идут регистры процессора.
- Выделенное зелёным – это инструкция ассемблера, т.е. команда с операндами. Именно её исполняет CPU.
- Ну и жёлтое это то, как видит инструкции процессор. Этот поток байт (каждая пара чисел) принято называть Operation-Code, или по нашему ‘Опкод’ инструкции.
0xb8
– это MOV AX
. Поскольку в ассемблере операнды всегда должны быть равны по размеру (а АХ это 2-байта), значит после 0хb8
нужно взять ещё 2-байта – итого три. Красиво эту картину расписал К.Касперски в своей статье “Дизассемблирование в уме”.1.3. Оперативная память ОЗУ.
Как упоминалось выше, для быстрого доступа к инструкциям кода, система считывает программу с жёсткого диска, в память ОЗУ. При этом, чтобы не забивать напрасно память, программа читается с диска не целиком, а кусками по 4 Кб – т.н. страницами. Когда процессор выполнит все инструкции этой страницы, процессор сгенерит исключение
#PF
(PageFault, ошибка страницы) и контроллёр-памяти сразу-же загрузит в память следующую страницу с диска, а отработанная отправится в утиль.На такой подкачке страниц с диска, основана виртуальная память защищённого режима. Например, можно по-очереди отображать в одну 4-Кбайтную страницу физической памяти, хоть 1000 страниц с диска и создастся иллюзия, как-будто на машине установлено 8-16 Gb физ.памяти. Правда работать такая программа будет как черепаха, т.к. всё время будет уходить на замещение страниц.
Процессор соединяется с памятью 64-битной шиной, однако выбор ячейки на модуле памяти требует двумерного адреса, т.е. адреса-строки, и адреса-столбца. Такой адрес назвали DRAM-адресом. Преобразованием адресов занимается контроллёр-памяти. Он разбивает линейный адрес процессора на две составляющие, и засылает их логике DDR по очереди – сначала строку, потом столбец. Чтобы логика модуля поняла где-какой адрес, контроллёр подтверждает их стробами #RAS (row-address-strobe) и #CAS (column, столбец) соответственно. Время между этими стробами (задержка) входит в состав ‘таймингов памяти’ и известно под названием RAS-to-CAS Delay.
В плане расхода тактов, обмен с динамической памятью DDR для процессора операция слишком медленная. Поэтому, чтобы не обращаться к ней часто, проц за один раз загребает сразу по 64-байта данных, которые помещает в свой кэш. Такой обмен назвали пакетным, а длину пакета задаёт параметр
BL
контроллёра памяти. Он расшифровывается как ‘BurstLength’ и может принимать значения 2,4,8,16,Page. По сути - это счётчик повторов. На всех исвестных мне виндах BL=8,
тогда при 64-битной (8-байтной) шине получаем пакет в 64 байта.Другими словами, при любых обстоятельствах (хоть запись, хоть чтение) процессор обменивается с памятью только 64-байтными кэш строками ‘Cache-Line’ в независимости от того, нужен ему байт или двойное слово. Он никогда не станет запрашивать шину ради одного байта, ведь шина – это общий ресурс, который может быть занят, например если кто-то в данный момент использует прямой обмен с памятью посредством DMA. Информацию о размере своей кэш-линейки можете почерпнуть из программы CPU-Z.
Основной проблемой DDR-SDRAM является время доступа к ней. Связано это с тем, что запоминающая матрица её ядра собрана на крошечных конденсаторах, время разряда которых физически не способно превысить частоту в 200 МГц. На более высоких частотах, кондёры просто не успевают полностью разряжаться, что приводит к искажению данных.
Но DRAM дешёвая и отказываться от неё пока рановато. Поэтому инженеры пошли другим путём, и ввели понятие 2n-prefetch (предвыборка). Суть фишки - в увеличении числа линий-чтения с матрицы. N считается тут степенью двойки. Например, для DDR2 n=2, или 2^2=4 параллельные линии, для DDR3 получаем уже 2^3=8 линий, для DDR4 соотвественно 16. Предвыбранные (prefetch) данные накапливаются в промежуточном буфере, который через мультиплексор MUX может сливать информацию с ядра, на любой частоте. Таким образом удалось решить проблему века, придерживаясь опорной частоты запоминающей матрицы в 200 МГц:
Вообще идея использовать конденсаторы для хранения битов не совсем удачна, т.к. они имеют свойства со-временем разряжаться. Значит нужно периодически считывать с них заряд, и записывать его обратно. Этот процесс известен как ‘регенерация ячеек памяти’. До модулей DDR2 регенерацией строк занимался контроллёр-памяти, а начиная с DDR3 - функцию перенесли внутрь DRAM-чипов. Теперь проблема ушла на второй план и выполняется в фоне. Конроллёр просто выделяет чипам определённое время на регенерацию, которое (среди таймингов) числится в розысках под кличкой ‘tRP’:
1.4. Кэш-память процессора.
Другое дело кэш процессора, где в роли запоминающих элементов выступают не конденсаторы, а пара транзисторов, включённых по схеме триггера. Такой тип памяти получил название ‘статическая’ или SRAM. Тут не нужно регенерировать ячейки, на то она и статическая. Процессор получает доступ к ней мгновенно, т.к. его регистры собраны на таких-же триггерах. Ему без разницы от куда брать данные, с другого регистра или со-своего кэш – скорость практически одинакова, если не учитывать небольшие издержки на внутреннюю шину.
Процессор имеет трёхуровневую организацию кеш: L1, L2 и L3. Первые два уровня физически разделены пополам – половина для кода, половина - для данных. Кэш L3 является общим и имеет наибольшую ёмкость памяти. Внутри своего кэш, процессор хранит только действительно нужную информацию и ту, что может потребоваться в будущем (предварительная загрузка данных). Именно поэтому он читает из памяти не единичными байтами, а прихватывает с собой сразу и последующие 63. Практика доказала, что программисты пишут код последовательно и лишь иногда появляются циклы. Значит кэш-контроллёр действительно знает своё дело, раз читает инфу пачками.
Структура и работа кэш памяти - довольная обширная тема, которая потянет на отдельную статью. Только кэш поддерживается полностью на аппаратном уровне, и асм-программисту доступны лишь некоторые средства работы с ним - например полная его очистка, когда хотим получить время выполнения (профайл) определённого участка кода. Поэтому мы не будем копать в эту тему глубоко, а позже - двинемся дальше..
Последнее редактирование: