Что такое дескрипторы в процессоре
Процессы и потоки, диспетчер задач windows, синхронизация потоков.
В этой статье мы поговорим на такие темы, как процессы и потоки, дискрипторы процесса, поговорим о синзронизации потоков и затронем всеми любимый диспетчер задач windows.
На протяжении существования процесса его выполнение может быть многократно прервано и продолжено. Для того, чтобы возобновить выполнение процесса, необходимо восстановить состояние его операционной среды. Состояние операционной среды отображается состоянием регистров и программного счетчика, режимом работы процессора, указателями на открытые файлы, информацией о незавершенных операциях ввода-вывода, кодами ошибок выполняемых данным процессом системных вызовов и т.д. Эта информация называется контекстом процесса.
Для того чтобы ОС могла управлять процессами, она должна располагать всей необходимой для этого информацией. С этой целью на каждый процесс заводится дескриптор процесса.
Дескриптор – специальная информационная структура, которая заводится на каждый процесс (описатель задачи, блок управления задачей).
В общем случае дескриптор содержит следующую информацию:
Дескриптор процесса по сравнению с контекстом содержит более оперативную информацию, которая должна быть легко доступна подсистеме планирования процессов. Контекст процесса содержит менее актуальную информацию и используется операционной системой только после того, как принято решение о возобновлении прерванного процесса.
Дескрипторы, как правило, постоянно располагаются в оперативной памяти с целью ускорить работу супервизора, который организует их в списки (очереди) и отображает изменение состояния процесса перемещением соответствующего описателя из одного списка в другой.
Для каждого состояния (за исключением состояния выполнения для однопроцессорной системы) ОС ведет соответствующий список задач, находящийся в этом состоянии. Однако для состояния ожидания может быть не один список, а столько, сколько различных видов ресурсов могут вызывать состояние ожидания.
Например, состояний ожидания завершения операции ввода/вывода может быть столько, сколько устройств ввода/вывода содержится в системе.
Процессы и потоки
Чтобы поддерживать мультипрограммирование, ОС должна определить и оформить для себя те внутренне единицы работы, между которыми будет разделяться процессор и другие ресурсы компьютера. В настоящее время в большинстве ОС определены два типа единиц работы:
В общем случае процессы просто никак не связаны между собой и могут принадлежать даже различным пользователям, разделяющим одну вычислительную систему. Другими словами, в случае процессов ОС считает их совершенно несвязанными и независимыми. При этом именно ОС отвечает за конкуренцию между процессами по поводу ресурсов.
Для повышения быстродействия процессов есть возможность задействовать внутренний параллелизм в самих процессах.
Например, некоторые операции, выполняемые приложением, могут требовать для своего исполнения достаточно длительного использования ЦП. В этом случае при интерактивной работе с приложением пользователь вынужден долго ожидать завершения заказанной операции и не может управлять приложением до тех пор, пока операция не выполнится до самого конца. Такие ситуации встречаются достаточно часто, например, при обработке больших изображений в графических редакторах. Если же программные модули, исполняющие такие длительные операции, оформлять в виде самостоятельных «подпроцессов» (потоков), которые будут выполняться параллельно с другими «подпроцессами», то у пользователя появляется возможность параллельно выполнять несколько операций в рамках одного приложения (процесса).
Можно выделить следующие отличия потоков от процессов:
Главное, что обеспечивает многопоточность, — это возможность параллельно выполнять несколько видов операций в одной прикладной программе. За счет чего реализуется эффективное использование ресурсов ЦП, а суммарное время выполнения задач становится меньше.
Например, если табличный процессор или текстовый процессор были разработаны с учетом возможностей многопоточной обработки, то пользователь может запросить пересчет своего рабочего листа или слияние нескольких документов и одновременно продолжать заполнять таблицу или открывать для редактирования следующий документ.
Диспетчер задач WINDOWS
В диспетчере задач отображаются сведения о программах и процессах, выполняемых на компьютере. Кроме того, там можно просмотреть наиболее часто используемые показатели быстродействия процессов.
Диспетчер задач служит для отображения ключевых показателей быстродействия компьютера. Для выполняемых программ можно просмотреть их состояние и завершить программы, переставшие отвечать на запросы. Имеется возможность просмотра активности выполняющихся процессов с использованием до 15 параметров, а также графиков и сведений об использовании ЦП и памяти.
Кроме того, если имеется подключение к сети, можно просматривать состояние сети и параметры ее работы. Если к компьютеру подключились несколько пользователей, можно увидеть их имена, какие задачи они выполняют, а также отправить им сообщение.
На вкладке Процессы отображаются сведения о выполняющихся на компьютере процессах: сведения об использовании ЦП и памяти, счетчике процессов и некоторые другие параметры:
На вкладке Быстродействие, отображаются сведения о счетчике дескрипторов и потоках, параметры памяти:
Потребность в синхронизации потоков возникает только в мультипрограммной ОС и связана с совместным использованием аппаратных и информационных ресурсов компьютера. Синхронизация необходима для исключения гонок (см. далее) и тупиков при обмене данными между потоками, разделении данных, при доступе к процессору и устройствам ввода-вывода.
Синхронизация потоков и процессов заключается в согласовании их скоростей путем приостановки потока до наступления некоторого события и последующей его активизации при наступлении этого события.
Пренебрежение вопросами синхронизации в многопоточной системе может привести к неправильному решению задачи или даже к краху системы.
Пример. Задача ведения базы данных клиентов некоторого предприятия.
Каждому клиенту отводится отдельная запись в базе данных, в которой имеются поля Заказ и Оплата. Программа, ведущая базу данных, оформлена как единый процесс, имеющий несколько потоков, в том числе:
Оба эти потока совместно работают над общим файлом базы данных, используя однотипные алгоритмы:
Обозначим шаги 1-3 для потока А как А1-А3, а для потока В как В1-В3. Предположим, что в некоторый момент поток А обновляет поле Заказ записи о клиенте N. Для этого он считывает эту запись в свой буфер (шаг А1), модифицирует значение поля Заказ (шаг А2), но внести запись в базу данных не успевает, так как его выполнение прерывается, например, вследствие истечение кванта времени.
Предположим, что потоку В также потребовалось внести сведения об оплате относительно того же клиента N. Когда подходит очередь потока В, он успевает считать запись в свой буфер (шаг В1) и выполнить обновление поля Оплата (шаг В2), а затем прерывается. Заметим, что в буфере у потока В находится запись о клиенте N, в которой поле Заказ имеет прежнее, не измененное значение.
Важным понятием синхронизации процессов является понятие «критическая секция» программы. Критическая секция — это часть программы, в которой осуществляется доступ к разделяемым данным. Чтобы исключить эффект гонок по отношению к некоторому ресурсу, необходимо обеспечить, чтобы в каждый момент в критической секции, связанной с этим ресурсом, находился максимум один процесс. Этот прием называют взаимным исключением.
Простейший способ обеспечить взаимное исключение — позволить процессу, находящемуся в критической секции, запрещать все прерывания. Однако этот способ непригоден, так как опасно доверять управление системой пользовательскому процессу; он может надолго занять процессор, а при крахе процесса в критической области крах потерпит вся система, потому что прерывания никогда не будут разрешены.
Другим способом является использование блокирующих переменных. С каждым разделяемым ресурсом связывается двоичная переменная, которая принимает значение 1, если ресурс свободен (то есть ни один процесс не находится в данный момент в критической секции, связанной с данным процессом), и значение 0, если ресурс занят. На рисунке ниже показан фрагмент алгоритма процесса, использующего для реализации взаимного исключения доступа к разделяемому ресурсу D блокирующую переменную F(D). Перед входом в критическую секцию процесс проверяет, свободен ли ресурс D. Если он занят, то проверка циклически повторяется, если свободен, то значение переменной F(D) устанавливается в 0, и процесс входит в критическую секцию. После того, как процесс выполнит все действия с разделяемым ресурсом D, значение переменной F(D) снова устанавливается равным 1.
Если все процессы написаны с использованием вышеописанных соглашений, то взаимное исключение гарантируется. Следует заметить, что операция проверки и установки блокирующей переменной должна быть неделимой. Поясняется это следующим образом. Пусть в результате проверки переменной процесс определил, что ресурс свободен, но сразу после этого, не успев установить переменную в 0, был прерван. За время его приостановки другой процесс занял ресурс, вошел в свою критическую секцию, но также был прерван, не завершив работы с разделяемым ресурсом. Когда управление было возвращено первому процессу, он, считая ресурс свободным, установил признак занятости и начал выполнять свою критическую секцию. Таким образом, был нарушен принцип взаимного исключения, что потенциально может привести к нежелаемым последствиям. Во избежание таких ситуаций в системе команд машины желательно иметь единую команду «проверка-установка», или же реализовывать системными средствами соответствующие программные примитивы, которые бы запрещали прерывания на протяжении всей операции проверки и установки.
Реализация критических секций с использованием блокирующих переменных имеет существенный недостаток: в течение времени, когда один процесс находится в критической секции, другой процесс, которому требуется тот же ресурс, будет выполнять рутинные действия по опросу блокирующей переменной, бесполезно тратя процессорное время. Для устранения таких ситуаций может быть использован так называемый аппарат событий. С помощью этого средства могут решаться не только проблемы взаимного исключения, но и более общие задачи синхронизации процессов. В разных операционных системах аппарат событий реализуется по-своему, но в любом случае используются системные функции аналогичного назначения, которые условно называются WAIT(x) и POST(x), где x — идентификатор некоторого события.
Если ресурс занят, то процесс не выполняет циклический опрос, а вызывает системную функцию WAIT(D), здесь D обозначает событие, заключающееся в освобождении ресурса D. Функция WAIT(D) переводит активный процесс в состояние ОЖИДАНИЕ и делает отметку в его дескрипторе о том, что процесс ожидает события D. Процесс, который в это время использует ресурс D, после выхода из критической секции выполняет системную функцию POST(D), в результате чего операционная система просматривает очередь ожидающих процессов и переводит процесс, ожидающий события D, в состояние ГОТОВНОСТЬ.
Обобщающее средство синхронизации процессов предложил Дейкстра, который ввел два новых примитива. В абстрактной форме эти примитивы, обозначаемые P и V, оперируют над целыми неотрицательными переменными, называемыми семафорами. Пусть S такой семафор. Операции определяются следующим образом:
V(S): переменная S увеличивается на 1 одним неделимым действием; выборка, инкремент и запоминание не могут быть прерваны, и к S нет доступа другим процессам во время выполнения этой операции.
P(S): уменьшение S на 1, если это возможно. Если S=0, то невозможно уменьшить S и остаться в области целых неотрицательных значений, в этом случае процесс, вызывающий P-операцию, ждет, пока это уменьшение станет возможным. Успешная проверка и уменьшение также является неделимой операцией.
В частном случае, когда семафор S может принимать только значения 0 и 1, он превращается в блокирующую переменную. Операция P заключает в себе потенциальную возможность перехода процесса, который ее выполняет, в состояние ожидания, в то время как V-операция может при некоторых обстоятельствах активизировать другой процесс, приостановленный операцией P.
Взаимоблокировка процессов
При организации параллельного выполнения нескольких процессов одной из главных функций ОС является корректное распределение ресурсов между выполняющимися процессами и обеспечение процессов средствами взаимной синхронизации и обмена данными.
При параллельном исполнении процессов могут возникать ситуации, при которых два или более процесса все время находятся в заблокированном состоянии. Самый простой случай – когда каждый из двух процессов ожидает ресурс, занятый другим процессом. Из-за такого ожидания ни один из процессов не может продолжить исполнение и освободить в конечном итоге ресурс, необходимый другому процессу. Эта тупиковая ситуация называется дедлоком (dead lock), тупиком, клинчем или взаимоблокировкой.
Говорят, что в мультизадачной системе процесс находится в состоянии тупика, если он ждет события, которое никогда не произойдет.
Тупиковые ситуации надо отличать от простых очередей, хотя и те и другие возникают при совместном использовании ресурсов и внешне выглядят похоже: процесс приостанавливается и ждет освобождения ресурса. Однако очередь — это нормальное явление, неотъемлемый признак высокого коэффициента использования ресурсов при случайном поступлении запросов. Она возникает тогда, когда ресурс недоступен в данный момент, но через некоторое время он освобождается, и процесс продолжает свое выполнение. Тупик же является в некотором роде неразрешимой ситуацией.
Проблема тупиков включает в себя следующие задачи:
Тупики могут быть предотвращены на стадии написания программ, то есть программы должны быть написаны таким образом, чтобы тупик не мог возникнуть ни при каком соотношении взаимных скоростей процессов. Так, если бы в предыдущем примере процесс А и процесс В запрашивали ресурсы в одинаковой последовательности, то тупик был бы в принципе невозможен. Второй подход к предотвращению тупиков называется динамическим и заключается в использовании определенных правил при назначении ресурсов процессам, например, ресурсы могут выделяться в определенной последовательности, общей для всех процессов.
В некоторых случаях, когда тупиковая ситуация образована многими процессами, использующими много ресурсов, распознавание тупика является нетривиальной задачей. Существуют формальные, программно-реализованные методы распознавания тупиков, основанные на ведении таблиц распределения ресурсов и таблиц запросов к занятым ресурсам. Анализ этих таблиц позволяет обнаружить взаимные блокировки.
Если же тупиковая ситуация возникла, то не обязательно снимать с выполнения все заблокированные процессы. Можно снять только часть из них, при этом освобождаются ресурсы, ожидаемые остальными процессами, можно вернуть некоторые процессы в область свопинга, можно совершить «откат» некоторых процессов до так называемой контрольной точки, в которой запоминается вся информация, необходимая для восстановления выполнения программы с данного места. Контрольные точки расставляются в программе в местах, после которых возможно возникновение тупика.
Защищенный режим работы процессора
Режимы адресации, дескрипторы
РЕЖИМ ВИРТУАЛЬНОЙ АДРЕСАЦИИ (РЕЖИМ ВА)
Механизм (схема) адресации
В формировании исполнительного адреса участвуют два компонента: 16-ти битовый селектор, используемый для определения адреса базы сегмента и 32-х битовый эффективный адрес.
Сформированный исполнительный адрес является физическим адресом, если не используется механизм страничной адресации. В противном случае, он преобразуется в физический. В режиме ВА селектор используется, как индекс, указывающий операционной системе на определенный элемент специальной таблицы, в которую помещен 32-х битовый адрес базы данного сегмента. Физический адрес формируется сложением базового адреса с 32-х битовым смещением.
Страничная адресация памяти является дополнительным механизмом управления памятью, используемым только в режиме ВА. Этот механизм позволяет преобразовывать исполнительные адреса в физические.
Сегментация
Таблицы дескрипторов
Таблицы дескрипторов определяют все сегменты, которые используются в системе на базе 80386.
Каждая из таблиц храниться в своей области памяти и имеет размер от 8 байт до 64 кбайт. Каждая таблица может содержать до 8к 8-ми байтных дескрипторов. Старшие 13 бит селектора используются в качестве индекса дескрипторной таблицы. Для обращения к таблицам имеются специальные регистры, хранящие 32-х битовый исполнительный адрес и 16 битовую границу данной таблицы.
Эти регистры GDTR, LDTR и IDTR загружаются командами LGDT, LLDT, LIDT и сохраняются командами SGDT, SLDT и SIDT. Манипуляция таблицами осуществляется операционной системой при помощи привилегированных команд.
Таблица глобальных дескрипторов (GDT)
Таблица локальных дескрипторов (LDT)
Таблица LDT содержит дескрипторы, используемые в данной задаче. Обычно ОС разрабатываются таким образом, чтобы каждая задача имела отдельную LDT. LDT может содержать только дескрипторы команд, данных, стека, номера задачи и вызова номера. Таблицы LDT служат механизмом для изоляции сегментов команд и данных отдельных задач от ОС, в то время, как GDT хранит дескрипторы сегментов, являющихся общими для всех задач. Сегмент не может быть доступен задаче, если его дескриптор отсутствует внутри текущей LDT или GTR. Это обеспечивает защиту сегментов задач и в то же время, совместное использование глобальных данных различными задачами. В отличие от 6-ти байтовых GDT и IDT регистров, которые хранят базовый адрес и ограничитель, видимая часть регистра LDT хранит только 16 битовый селектор. Этот селектор определяет дескриптор LDT в GDT.
Таблица дескрипторов прерываний (IDT)
IDT содержит дескрипторы, указывающие расположение 256 подпрограмм обслуживания прерываний. IDT может хранить только вектора задач, вектора прерываний и вектора трассировок. IDT должна быть не менее, чем 256 байтовой, чтобы хранить дескрипторы 32 прерываний, зарезервированных ф. Intel. Каждое прерывание, используемое в системе, должно быть описано в IDT. IDT используется при выполнении команд INT, внешних запросов прерываний в особых ситуациях.
Дескрипторы
Биты атрибутов дескриптора
Все сегменты 80386 имеют 3 единых поля: P, DPL, S.
Дескрипторы команд и данных (S=1)
Формат такого дескриптора и байт прав доступа приведены на рис.4-6 и табл.4-1 соответственно.
Дескрипторы сегментов команд и данных имеют несколько общих полей: A, G.
Система на базе 80386 может включать сегменты как байтовой размерностью, так и со страничной размерностью, если включен блок страничной адресации.
Бит Е указывает, какой из сегментов выполняется: командный (E=1, S=1) или данных (E=0, S=1).
Кодовый сегмент может находится в стадии только выполнения (бит R=0) или выполнения/чтения (R=1). Запись в кодовый сегмент не возможна.
Замечания.
Кодовые сегменты могут изменяться, через вымышленные имена (метки). Метки приписываются сегментам данных, которые располагаются в некотором пространстве исполнительных адресов, как сегменты команд.
Бит D указывает размерность операндов и эффективных адресов (32 бита при D=1, 16 бит при D=0). Это позволяет выполнять команды для 80286.
Другим атрибутом сегментов команд является бит согласования С. При С=1 согласованные сегменты могут выполняться в режиме разделения программами, имеющими различные уровни привилегированности.
Сегменты, определяемые как сегменты данных (E=0, S=1), могут быть двух типов: сегменты стека и сегменты данных. Бит направления расширения (ED) определяет тип сегмента. Если сегмент стековый, то смещение должно быть больше, чем граница сегмента. Для сегмента данных смещение должно быть меньше или равно границе сегмента. Другими словами, сегменты стека начинаются от базы исполнительного адреса + максимального размера сегмента и размещаются в сторону уменьшения до базы исполнительного адреса + граница. Сегмент данных начинается с базы исполнительного адреса и кончается границей сегмента.
Бит записи W управляет записью в сегменте. При W=0 сегменты данных работают только на чтение. Стековые сегменты должны иметь W=1.
Форматы системных дескрипторов
Эти дескрипторы содержат информацию о системных таблицах, задачах и паролях. Системный дескриптор 80386 имеет 32 разрядную базу и 20 битовую границу, системный дескриптор 80286 соответствует 24 и 16 бит (старшие 16 бит равны 0).
Дескрипторы LDT (S=0, TYPE=2)
Дескрипторы LDT содержат информацию о таблицах локальных дескрипторов. Таблица локальных дескрипторов содержит дескрипторы сегментов, используемых конкретной задачей. Команда загрузки регистра LDT должна иметь нулевой уровень привилегированности (поле DPL игнорируется).
Дескрипторы TSS (S=0, TYPE = 1, 3, 9, B)
Дескрипторы шлюзов (S=0, TYPE=4-7, C, F)
Шлюзы используются для управления доступом к определенным точкам внутри управляющего кодового сегмента.
Шлюзы реализуют дисциплину взаимодействия при управлении передачей между источником и приемником. При взаимодействии процессор автоматически проверяет защиту. Это также позволяет проектировщикам системы управлять точками внутри ОС. Шлюзы вызова используются для изменения уровней привилегированности. Шлюзы задач используются для переключения задач, а шлюзы прерываний и трассировок используются для определения специальных программ обслуживания прерываний.
Шлюз вызова, главным образом, используются для передачи управления программам с более высоким уровнем привилегированности.
Поле счетчика слов используется только в шлюзах вызыва, которые изменяют уровень привилегированности, в остальных типах это поле игнорируется.
Шлюзы прерываний и трассировок используют поля селектора назначения и смещения назначения дескриптора для указания начала программ управления прерыванием или трассировкой. Различия между шлюзами прерываний и шлюзами трассировок заключается в том, что шлюз прерывание выключает прерывания (сбрасывает бит IF) в то время, как шлюз трассировки нет.
Шлюзы задач используются при переключении задач. Шлюзы задач могут обращаться только к сегменту состояния задач (TSS), поэтому используется только селекторная часть а смещение игнорируется.
Прерывание 13 формируется, когда селектор назначения указывает на некорректный тип дескриптора.
Поле селектора
В дополнение к величине селектора каждый сегментный регистр имеет связанный с ним регистр (КЭШ ЗУ) дескриптора сегмента. Когда происходит изменение содержимого сегментного регистра, 8-ми байтный дескриптор, связанный с этим селектором, автоматически переписывается в процессор. Эта информация используется до тех пор, пока не потребуется доступ к другому сегменту. Содержимое дескрипторных регистров программно недоступно (невидимо для программистов). При программном изменении дескрипторных таблиц, хранящихся в ЗУ, необходимо осуществлять перезагрузку дескрипторных регистров.
Форматы регистров дескрипторов
Содержимое этих регистров зависит от режима работы МП. Формат регистров для режима RM приведен на рис.4-11.
Формат регистров в режиме РМ представлен на рис.4-12.
В РМ значения полей определяется содержимым дескриптора сегмента индексированного селектором.
Формат регистра для режима виртуального 8086 приведен на рис.4-13.
В отличие от режима RM виртуальная программа имеет минимальный уровень привилегированности (=3), обеспечивая трассировку всех команд IOPL и команд с нулевым уровнем привилегированности.
Организация памяти
За последнюю неделю дважды объяснял людям как организована работа с памятью в х86, с целью чтобы не объяснять в третий раз написал эту статью.
И так, чтобы понять организацию памяти от вас потребуется знания некоторых базовых понятий, таких как регистры, стек и тд. Я по ходу попробую объяснить и это на пальцах, но очень кратко потому что это не тема для этой статьи. Итак начнем.
Как известно программист, когда пишет программы работает не с физическим адресом, а только с логическим. И то если он программирует на ассемблере. В том же Си ячейки памяти от программиста уже скрыты указателями, для его же удобства, но если грубо говорить указатель это другое представление логического адреса памяти, а в Java и указателей нет, совсем плохой язык. Однако грамотному программисту не помешают знания о том как организована память хотя бы на общем уровне. Меня вообще очень огорчают программисты, которые не знают как работает машина, обычно это программисты Java и прочие php-парни, с квалификацией ниже плинтуса.
Так ладно, хватит о печальном, переходим к делу.
Рассмотрим адресное пространство программного режима 32 битного процессора (для 64 бит все по аналогии)
Адресное пространство этого режима будет состоять из 2^32 ячеек памяти пронумерованных от 0 и до 2^32-1.
Программист работает с этой памятью, если ему нужно определить переменную, он просто говорит ячейка памяти с адресом таким-то будет содержать такой-то тип данных, при этом сам програмист может и не знать какой номер у этой ячейки он просто напишет что-то вроде:
int data = 10;
компьютер поймет это так: нужно взять какую-то ячейку с номером стопицот и поместить в нее цело число 10. При том про адрес ячейки 18894 вы и не узнаете, он от вас будет скрыт.
Все бы хорошо, но возникает вопрос, а как компьютер ищет эту ячейку памяти, ведь память у нас может быть разная:
3 уровень кэша
2 уровень кэша
1 уровень кэша
основная память
жесткий диск
Это все разные памяти, но компьютер легко находит в какой из них лежит наша переменная int data.
Этот вопрос решается операционной системой совместно с процессором.
Вся дальнейшая статья будет посвящена разбору этого метода.
Архитектура х86 поддерживает стек.
Стек это непрерывная область оперативной памяти организованная по принципу стопки тарелок, вы не можете брать тарелки из середины стопки, можете только брать верхнюю и класть тарелку вы тоже можете только на верх стопки.
В процессоре для работы со стеком организованны специальные машинные коды, ассемблерные мнемоники которых выглядят так:
push operand
помещает операнд в стек
pop operand
изымает из вершины стека значение и помещает его в свой операнд
Стек в памяти растет сверху вниз, это значит что при добавлении значения в него адрес вершины стека уменьшается, а когда вы извлекаете из него, то адрес вершины стека увеличивается.
Теперь кратко рассмотрим что такое регистры.
Это ячейки памяти в самом процессоре. Это самый быстрый и самый дорогой тип памяти, когда процессор совершает какие-то операции со значением или с памятью, он берет эти значения непосредственно из регистров.
В процессоре есть несколько наборов логик, каждая из которых имеет свои машинные коды и свои наборы регистров.
Basic program registers (Основные программные регистры) Эти регистры используются всеми программами с их помощью выполняется обработка целочисленных данных.
Floating Point Unit registers (FPU) Эти регистры работают с данными представленными в формате с плавающей точкой.
Еще есть MMX и XMM registers эти регистры используются тогда, когда вам надо выполнить одну инструкцию над большим количеством операндов.
Рассмотрим подробнее основные программные регистры. К ним относятся восемь 32 битных регистров общего назначения: EAX, EBX, ECX, EDX, EBP, ESI, EDI, ESP
Для того чтобы поместить в регистр данные, или для того чтобы изъять из регистра в ячейку памяти данные используется команда mov:
mov eax, 10
загружает число 10 в регистр eax.
mov data, ebx
копирует число, содержащееся в регистре ebx в ячейку памяти data.
Регистр ESP содержит адрес вершины стека.
Кроме регистров общего назначения, к основным программным регистрам относят шесть 16битных сегментных регистров: CS, DS, SS, ES, FS, GS, EFLAGS, EIP
EFLAGS показывает биты, так называемые флаги, которые отражают состояние процессора или характеризуют ход выполнения предыдущих команд.
В регистре EIP содержится адрес следующей команды, которая будет выполнятся процессором.
Я не буду расписывать регистры FPU, так как они нам не понадобятся. Итак наше небольшое отступление про регистры и стек закончилось переходим обратно к организации памяти.
Как вы помните целью статьи является рассказ про преобразование логической памяти в физическую, на самом деле есть еще промежуточный этап и полная цепочка выглядит так:
линейный адрес=Базовый адрес сегмента(на картинке это начало сегмента) + смещение
Сегмент кода
Сегмент данных
Сегмент стека
Используемый сегмент стека задается значением регистра SS.
Смещение внутри этого сегмента представлено регистром ESP, который указывает на вершину стека, как вы помните.
Сегменты в памяти могут друг друга перекрывать, мало того базовый адрес всех сегментов может совпадать например в нуле. Такой вырожденный случай называется линейным представлением памяти. В современных системах, память как правило так организована.
Теперь рассмотрим определение базовых адресов сегмента, я писал что они содержаться в регистрах SS, DS, CS, но это не совсем так, в них содержится некий 16 битный селектор, который указывает на некий дескриптор сегментов, в котором уже хранится необходимый адрес.
Так выглядит селектор, в тринадцати его битах содержится индекс дескриптора в таблице дескрипторов. Не хитро посчитать будет что 2^13 = 8192 это максимальное количество дескрипторов в таблице.
Вообще дескрипторных таблиц бывает два вида GDT и LDT Первая называется глобальная таблица дескрипторов, она в системе всегда только одна, ее начальный адрес, точнее адрес ее нулевого дескриптора хранится в 48 битном системном регистре GDTR. И с момента старта системы не меняется и в свопе не принимает участия.
А вот значения дескрипторов могут меняться. Если в селекторе бит TI равен нулю, тогда процессор просто идет в GDT ищет по индексу нужный дескриптор с помощью которого осуществляет доступ к этому сегменту.
Пока все просто было, но если TI равен 1 тогда это означает что использоваться будет LDT. Таблиц этих много, но использоваться в данный момент будет та селектор которой загружен в системный регистр LDTR, который в отличии от GDTR может меняться.
Индекс селектора указывает на дескриптор, который указывает уже не на базовый адрес сегмента, а на память в котором хранится локальная таблица дескрипторов, точнее ее нулевой элемент. Ну а дальше все так же как и с GDT. Таким образом во время работы локальные таблицы могут создаваться и уничтожаться по мере необходимости. LDT не могут содержать дескрипторы на другие LDT.
Итак мы знаем как процессор добирается до дескриптора, а что содержится в этом дескрипторе посмотрим на картинке:
Дескрипторы состоит из 8 байт.
Биты с 15-39 и 56-63 содержат линейный базовый адрес описываемым данным дескриптором сегмента. Напомню нашу формулу для нахождения линейного адреса:
линейный адрес = базовый адрес + смещение
[база; база+предел)
(база+предел; вершина]
Кстати интересно почему база и предел так рвано располагаются в дескрипторе. Дело в том что процессоры х86 развивались эволюционно и во времена 286х дескрипторы были по 8 бит всего, при этом старшие 2 байта были зарезервированы, ну а в последующих моделях процессоров с увеличением разрядности дескрипторы тоже выросли, но для сохранения обратной совместимости пришлось оставить структуру как есть.
Значение адреса «вершина» зависит от 54го D бита, если он равен 0, тогда вершина равна 0xFFF(64кб-1), если D бит равен 1, тогда вершина равна 0xFFFFFFFF (4Гб-1)
С 41-43 бит кодируется тип сегмента.
000 — сегмент данных, только считывание
001 — сегмент данных, считывание и запись
010 — сегмент стека, только считывание
011 — сегмент стека, считывание и запись
100 — сегмент кода, только выполнение
101- сегмент кода, считывание и выполнение
110 — подчиненный сегмент кода, только выполнение
111 — подчиненный сегмент кода, только выполнение и считывание
44 S бит если равен 1 тогда дескриптор описывает реальный сегмент оперативной памяти, иначе значение S бита равно 0.
Самым важным битом является 47-й P бит присутствия. Если бит равен 1 значит, что сегмент или локальная таблица дескрипторов загружена в оперативку, если этот бит равен 0, тогда это означает что данного сегмента в оперативке нет, он находится на жестком диске, случается прерывание, особый случай работы процессора запускается обработчик особого случая, который загружает нужный сегмент с жесткого диска в память, если P бит равен 0, тогда все поля дескриптора теряют смысл, и становятся свободными для сохранения в них служебной информации. После завершения работы обработчика, P бит устанавливается в значение 1, и производится повторное обращение к дескриптору, сегмент которого находится уже в памяти.
На этом заканчивается преобразование логического адреса в линейный, и я думаю на этом стоит прерваться. В следующий раз я расскажу вторую часть преобразования из линейного в физический.
А так же думаю стоит немного поговорить о передачи аргументов функции, и о размещении переменных в памяти, чтобы была какая-то связь с реальностью, потому размещение переменных в памяти это уже непосредственно, то с чем вам приходится сталкиваться в работе, а не просто какие-то теоретические измышления для системного программиста. Но без понимания, как устроена память невозможно понять как эти самые переменные хранятся в памяти.
В общем надеюсь было интересно и до новых встреч.