Какие сегменты имеет память
Перейти к содержимому

Какие сегменты имеет память

  • автор:

СЕГМЕНТ ПАМЯТИ

СЕГМЕНТ ПАМЯТИ, сегмент (memory segment). 1. Часть оперативной памяти фиксированного размера, используемая при формировании адресного пространства центрального процессора или задачи таким образом, что для аппаратуры адрес памяти состоит из двух частей: номера (имени) сегмента и смещения внутри сегмента. Обычно С. п. имеет размер максимально возможного смещения (часто это 64 Кб). См. распределение памяти. 2. Логическая или физическая единица подкачки в системе с виртуальной памятью. Часть физической или виртуальной памяти, используемая как единое целое при перемещении участков программ или данных из внешней памяти в оперативную память и обратно. С. п. отличается от страницы тем, что его размер не зафиксирован, и пользователь в определенной мере может влиять на этот размер. См. виртуальная память, подкачка, свопинг

Сегментная и страничная организация памяти — WEBSITE X5 UNREGISTERED VERSION 12.0.5.22 — Электронный справочник по дисциплине Операционные системы и среды

Шина адреса — компьютерная шина, используемая центральным процессором или устройствами, способными инициировать сеансы DMA, для указания физическогоадреса слова ОЗУ (или начала блока слов), к которому устройство может обратиться для проведения операции чтения или записи.

Основной характеристикой шины адреса является её ширина в битах. Ширина шины адреса определяет объём адресуемой памяти. Например, если ширина адресной шины составляет 20 бит, и размер слова памяти равен одному байту (минимальный адресуемый объём данных), то объём памяти, который можно адресовать, составляет 2 20 = 1 048 576 байтов (1 МБайт) как в IBM PC/XT.

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

Если рассматривать структурную схему микро-ЭВМ, то адресная шина активизирует работу всех внешних устройств по команде, которая поступает с микропроцессора.

Страничная и сегментная организация памяти.
Страничная память

Описанные выше схемы недостаточно эффективно используют память, поэтому в современных схемах управления памятью не принято размещать процесс в оперативной памяти одним непрерывным блоком.

В самом простом и наиболее распространенном случае страничной организации памяти (или paging) как логическое адресное пространство, так и физическое представляются состоящими из наборов блоков илистраниц одинакового размера. При этом образуются логические страницы (page), а соответствующие единицы вфизической памяти называют физическими страницами или страничными кадрами (page frames). Страницы (и страничные кадры) имеют фиксированную длину, обычно являющуюся степенью числа 2, и не могут перекрываться. Каждый кадр содержит одну страницу данных. При такой организации внешняя фрагментацияотсутствует, а потери из-за внутренней фрагментации, поскольку процесс занимает целое число страниц, ограничены частью последней страницы процесса.

Логический адрес в страничной системе – упорядоченная пара (p,d), где p – номер страницы в виртуальной памяти, а d – смещение в рамках страницы p, на которой размещается адресуемый элемент. Заметим, что разбиение адресного пространства на страницы осуществляется вычислительной системой незаметно для программиста. Поэтому адрес является двумерным лишь с точки зрения операционной системы, а с точки зрения программиста адресное пространство процесса остается линейным.

Описываемая схема позволяет загрузить процесс, даже если нет непрерывной области кадров, достаточной для размещения процесса целиком. Но одного базового регистра для осуществления трансляции адреса в данной схеме недостаточно. Система отображения логических адресов в физические сводится к системе отображения логических страниц в физические и представляет собой таблицу страниц, которая хранится в оперативной памяти. Иногда говорят, что таблица страниц – это кусочно-линейная функция отображения, заданная в табличном виде.

Таблица страниц (page table) адресуется при помощи специального регистра процессора и позволяет определить номер кадра по логическому адресу. Помимо этой основной задачи, при помощи атрибутов, записанных в строке таблицы страниц, можно организовать контроль доступа к конкретнойстранице и ее защиту.

Отметим еще раз различие точек зрения пользователя и системы на используемую память. С точки зрения пользователя, его память – единое непрерывное пространство, содержащее только одну программу. Реальное отображение скрыто от пользователя и контролируется ОС. Заметим, что процессу пользователя чужая память недоступна. Он не имеет возможности адресовать память за пределами своей таблицы страниц, которая включает только его собственные страницы.

Для управления физической памятью ОС поддерживает структуру таблицы кадров. Она имеет одну запись на каждый физический кадр, показывающий его состояние.

Отображение адресов должно быть осуществлено корректно даже в сложных случаях и обычно реализуется аппаратно. Для ссылки на таблицу процессов используется специальный регистр. При переключении процессов необходимо найти таблицу страниц нового процесса, указатель на которую входит в контекст процесса.

Сегментная и сегментно-страничная организация памяти

Существуют две другие схемы организации управления памятью: сегментная и сегментно-страничная. Сегменты, в отличие от страниц, могут иметь переменный размер. Идея сегментации изложена во введении. При сегментной организации виртуальный адрес является двумерным как для программиста, так и для операционной системы, и состоит из двух полей – номера сегмента и смещения внутри сегмента.

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

Программисты, пишущие на языках низкого уровня, должны иметь представление о сегментной организации, явным образом меняя значения сегментных регистров (это хорошо видно по текстам программ, написанных на Ассемблере). Логическое адресное пространство – набор сегментов. Каждыйсегмент имеет имя, размер и другие параметры (уровень привилегий, разрешенные виды обращений, флаги присутствия). В отличие от страничной схемы, где пользователь задает только один адрес, который разбивается на номер страницы и смещение прозрачным для программиста образом, в сегментной схеме пользователь специфицирует каждый адрес двумя величинами: именем сегмента и смещением.

Каждый сегмент – линейная последовательность адресов, начинающаяся с 0. Максимальный размер сегмента определяется разрядностью процессора (при 32-разрядной адресации это 2 32 байт или 4 Гбайт). Размер сегмента может меняться динамически (например, сегмент стека). В элементе таблицысегментов помимо физического адреса начала сегмента обычно содержится и длина сегмента. Если размер смещения в виртуальном адресе выходит за пределы размера сегмента, возникает исключительная ситуация.

Логический адрес – упорядоченная пара v=(s,d), номер сегмента и смещение внутри сегмента.

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

7.1 Лекция — Сегментация памяти.

Рассмотрим пример, когда программа использует одно адресное пространство.

программа использует одно адресное пространство

Недостатки такой системы:

Один участок может полностью заполниться, но при этом останутся свободные участки. Можно конечно перемещать участки, но это очень сложно.

Эти проблемы можно решить, если дать каждому участку независимое адресное пространство, называемое сегментом.

Рассмотрим то же пример с использованием сегментов:

Каждый сегмент может расти или уменьшаться независимо от других.

Сегмент — это логический объект.

В этом случае адрес имеет две части:

  • номер сегмента
  • адрес в сегменте
  • Сегменты не мешают друг другу.
  • Начальный адрес процедуры всегда начинается с (n,0). Что упрощает программирование.
  • Облегчает совместное использование процедур и данных.
  • Раздельная защита каждого сегмента (чтение, запись).

8.2 Реализация сегментации

Если страницы имеют фиксированный размер, то сегменты нет.

У сегментов так же, как и у страниц, существует проблема фрагментации.

Т.к. памяти часто не хватает, стали использовать страничную организацию сегментов. При которой в памяти может находиться только часть сегмента.

8.2.1 Сегментация с использованием страниц: MULTICS

В одной из первых, где была применена страничная сегментация, была система MULTICS .

Каждая программа обеспечивалась до 2^18 сегментов (более 250 000), каждый из которых мог быть до 65 536 (36-разрядных) слов длиной.

Таблица сегментов — хранит дескриптор для каждого сегмента. У каждой программы своя таблица.

Т.к. записей в таблице более 250 000, она сама разбита на страницы.

Сама таблица является отдельным сегментом.

Сегмент с таблицей дескрипторов указывающих на таблицы страниц для каждого сегмента

Нормальный размер страницы равен 1024 словам. Если сегмент меньше 1024, то он либо не разбит на страницы, либо разбит на страницы по 64 слова.

Когда происходит обращение к памяти, выполняется следующий алгоритм:

  1. По номеру сегмента находится дескриптор сегмента.
  2. Проверяется, находиться ли таблица страницы в памяти. Если в памяти, определяется ее расположение. Если нет, вызывается сегментное прерывание.
  3. Проверяется, находиться ли страница в памяти. Если в памяти, определяется ее расположение в памяти. Если нет в памяти, вызывается страничное прерывание.
  4. К адресу начала страницы прибавляется смещение, в результате получаем адрес нужного слова в оперативной памяти.
  5. Происходит запись или чтение.

Преобразование адреса в системе MULTICS

Так как такой алгоритм будет работать достаточно медленно. Аппаратура системы MULTICS содержит высокоскоростной буфер быстрого преобразования адреса (TLB) размером в 16 слов. Адреса 16 наиболее часто использующихся страниц хранятся в буфере.

8.2.2 Сегментация с использованием страниц: Intel Pentium

Каждая программа обеспечивается до 16К сегментов, каждый из которых может быть до 1 млдр 36-разрядных слов длиной.

Основа виртуальной памяти системы Pentium состоит из двух таблиц:

  • Локальная таблица дескрипторов LDT (Local Descriptor Table) — есть у каждой программы, и описывает сегменты программы.
  • Глобальная таблица дескрипторов GDT (Global Descriptor Table) — одна для всех программ, и описывает системные сегменты (включая саму ОС).

Каждый селектор (указывает на дескриптор) представляет собой 16-разрядный номер.

Селектор в системе Pentium

13 битов определяют номер записи в таблице дескрипторов, поэтому эти таблицы ограничены, каждая содержит 8К (2^13) сегментных дескрипторов.

1 бит указывает тип используемой таблицы дескрипторов LDT или GDT.

Уровни привилегированности в системе Pentium

Уровни привилегированности запрещают выполняемому коду обратиться к более низкому уровню.

С учетом максимального размера сегмента — 4 Гбайта — каждая задача, при чисто сегментной организации виртуальной памяти, работает в виртуальном адресном пространстве в 64 Тбайта (4 Гбайта * 16К, где 16К=8К*2 т.к. LDT и GDT).

Дескриптор программного (не данных) сегмента в системе Pentium (всего 8 байт (64 бита)).

База (Base) — базовый адрес сегмента (32-бита), разделен на три части из-за совместимости с i286, в котором это поле имеет только 24 бита.

Размер (Limit) — размер сегмента (20 бит), разнесен на две части.

Если размер сегмента указан в страницах, он может достигать 2^32 байтов (2^20 * 4Кбайт (2^12) (размер страницы в Pentium)).

Алгоритм получение физического адреса:

  1. Селектор загружается в регистр (для сегмента команд в CS, для сегмента данных в DS).
  2. Определяется глобальный или локальный сегмент (LDT или GDT).
  3. Дескриптор извлекается из LDT или GDT, и сохраняется в микропрограммных регистрах.
  4. Если дескриптор в памяти и смещение не выходит за пределы сегмента, программа может продолжить работу, если нет, происходит прерывание.
  5. Система Pentium прибавляет базовый адрес к смещению, и получает линейный адрес,
    если страничная организация памяти не используется, то он является физическим адресом (адрес получен),
    — если страничная организация памяти используется, то он является виртуальным адресом.
  6. В случае, если используется страничная организация памяти, линейный адрес переводится в физический с помощью таблицы страниц.

Преобразование пары (селектора, смещение) в физический адрес

При 32-разрядном (2^32=4Гбайт) адресе и 4Кбатной странице, сегмент может содержать 1 млн страниц (4Гбайт/4Кбайта). Поэтому используется двухуровневое отображение (создана таблица (страничный каталог) содержащая список из 1024 таблиц страниц), благодаря чему можно снизить количество записей в таблице страниц до 1024.

В этом случае сегмент в 4 Мбайта (1024 записи по 4 Кбайта страницы), будет иметь страничный каталог только с одной записью (и 1024 в таблице страниц), вместо 1 млн в одной таблице.

Отображение линейного адреса на физический адрес

В системе Pentium также есть буфер быстрого преобразования адреса (TLB), в котором хранятся наиболее часто используемые комбинации Каталог-Страница на физический адрес страничного блока. Только если комбинация в TLB отсутствует, выполняется это алгоритм.

8.3 Особенности реализации в UNIX

В LUNIX системе на 32-разрядной машине каждый процесс получает 3Гбайта виртуального пространства для себя, и 1Гбайт для страничных таблиц и других данных ядра.

На компьютерах Pentium, используется двухуровневые таблицы страниц, и размер страниц фиксирован 4Кбайта

На компьютерах Alpha, используется трехуровневые таблицы страниц, и размер страниц фиксирован 8Кбайт

Для каждой программы выделяется 3 сегмента:

  1. Код программы (только для чтения)
  2. Данные
  3. Стек

Сегментация памяти (Схема памяти компьютера)

Представляю, Вам, перевод статьи одного из разработчиков PHP, в том числе версии 7 и выше, сертифицированного инженера ZendFramework. В данный момент работает в SensioLabs и большую часть занимается низкоуровневыми вещами, в том числе программированием в С под Unix. Оригинал статьи здесь.

Ошибка Сегментации: (Компьютерная верстка памяти)

Несколько слов, о чем эта запись в блоге

Я планирую в будущем писать технические статьи о PHP, связанные с глубоким пониманием памяти. Мне нужно, чтобы мои читатели имели такие знания, которые им помогут понять некоторые концепции дальнейшего объяснения. Для того, чтобы ответить на этот вопрос, нам придется перемотать время назад в 1960-е года. Я собираюсь объяснить вам, как работает компьютер, а точнее, как происходит доступ к памяти в современном компьютере, а затем вы поймете, из-за чего происходит это странное сообщение об ошибке — Segmentation Fault.

То, что вы будете читать здесь, краткое изложение основ дизайна компьютерной архитектуры. Я не буду заходить слишком далеко, если это не нужно, и буду использовать хорошо известные формулировки, так что, кто работает с компьютером каждый день может понять такие важные понятия о том, как работает ПК. Существует много книг о компьютерной архитектуре. Если вы хотите углубиться дальше в этой теме, я предлагаю вам достать некоторые из них и начать читать. Кроме того, откройте исходный код ядра ОС и изучите его, будь то ядро Linux, или любое другое.

Немного истории computer science

Еще в то время, когда компьютерами были огромные машины, тяжелее тонны, внутри вы могли бы найти один процессор с чем-то вроде 16Kb оперативной памяти (RAM). Не будем углубляться дальше =) В этот период компьютер стоил примерно $ 150 000 и мог выполнить ровно одну задачу в момент времени. Если бы мы в то время нарисовали схему его памяти, то архитектура выглядела следующим образом:

image

  • Область памяти операционной системы(4kb)
  • Область памяти запущенных процессов (12Kb)

Но одна из главных проблем такой модели заключается в том, что компьютер (стоимостью $ 150 000) мог выполнять только одну задачу единовременно, и эта задача ужасно долго выполнялась: целые дни, чтобы обработать несколько Кб данных. По такой огромной цене явно не представляется возможным купить несколько машин, чтобы выполнить несколько процедур одновременно. Так что люди пытались распределить ресурсы машины. Это было время рождения многозадачности. Имейте в виду, что тогда было ещё очень рано говорить о многопроцессорных ПК. Как мы можем заставить одну машину с одним процессором, и на самом деле решить несколько различных задач? Ответ на этот вопрос — планирование. Пока один процесс занят в ожидании ввода / вывода (ожидает прерывание), процессор может запустить другой процесс. Мы не будем говорить о планировании на всех уровнях (слишком широкой теме), только о памяти. Если машина может выполнять несколько задач, один процесс за другим, то это означает, что память будет распределяться примерно таким образом:

image

Обе, задачи А и B, сохраняются в оперативной памяти, так как копировать их туда и обратно на диск — слишком ресурсозатратный процесс. Данные должны оставаться в оперативной памяти на постоянной основе, так как их соответствующие задачи по-прежнему работают. Планировщик дает некоторое время процессора, то задаче А, то B, и т.д… каждый раз предоставляя доступ к её области памяти. Но подождите, здесь есть проблема. Когда один программист будет писать код задачи B, он должен знать адреса границ памяти. Например, задача В будет располагаться с 10Kb памяти до 12Кб. Поэтому каждый адрес в программе должен жестко записан именно в эти пределы. Если машина затем будет выполнять ещё 3 задачи, то адресное пространство будет разделено на большее количество областей, и границы памяти задачи В сдвинутся. Программисту пришлось бы переписать свою программу, чтобы использовать меньше памяти, и переписывать каждый адрес указателя памяти.

Здесь очевидна так же другая проблема: что, если задача B получит доступ к памяти задачи А? Это легко может произойти, потому что, когда вы манипулируете указателями, небольшая ошибка при вычислении приводит к совершенно другому адресу. Это может испортить данные задачи А (перезаписать их). Также существует проблема безопасности, что если задача А работает с очень чувствительными данными? Нет никакого способа, чтобы предотвратить чтение задачей B некоторой области памяти задачи А. И последнее: что, если задача B по ошибке перезапишет память ОС? Например, память ОС от 0Kb до 4Kb, в случае если задача B перепишет этот участок, то операционная система наверняка потерпит крах.

Адресное пространство

Таким образом, нужна помощь операционной системы и аппаратного обеспечения, чтобы получить возможность запускать несколько задач находящиеся в памяти. Один из способов помочь, является создание того, что называется адресное пространство. Адресное пространство является абстракцией памяти, которую операционная система даст процессу, и эта концепция является абсолютно фундаментальной, так как в настоящее время каждая часть компьютера, которую вы встретите в вашей жизни, разработана таким образом. Сейчас не существует никакой другой модели (в гражданском обществе, армия может сохранять тайну).

Каждая система в настоящее время организована с таким макетом памяти как “код — стек — куча”, и это расстраивает.

Адресное пространство содержит все задачи (процессы) которые необходимо запустить:

  • Код: это машинные инструкции, которые процессор должен обработать
  • Данные: данные машинных команд которые обрабатываются вместе с ними.

image

  • Стек — это область памяти, где программа хранит информацию о вызываемых функциях, их аргументах и каждой локальной переменной в функции.
  • Куча — это область памяти где программист волен делать всё что угодно.
  • Код — область памяти, где будут храниться инструкции ЦП скомпилированной программы. Эти инструкции генерируются компилятором, но могут быть написаны вручную. Обратите внимание, что сегмент кода обычно делится на три части (текст, данные и BSS), но мы не будем так глубоко погружаться.
  • Код всегда фиксированного размера создаётся компилятором и будет весить (в нашем примере) 1Кб.
  • Стек, однако, является изменяемой зоной, так как программа работает. Когда функции вызываются, стек расширяется, при возвращении из функции: стек уменьшается.
  • Куча тоже является изменяемой зоной, когда программист выделяет память из кучи (malloc()), она расширяется, а когда программист освобождает память обратно (free()), куча сужается.

Виртуализация памяти

Если задача А получила адресное пространство, такое, которое мы видели, так же как и задача B. То как мы можем разместить их в памяти? Это кажется странным, но адресное пространство задачи А начинается от 0KB до 16Kb, так же как и задачи B. Хитрость заключается в создании виртуальной среды. На самом деле картина размещения А и В в памяти:

image

Когда задача А попытается получить доступ к памяти в своем адресном пространстве, например индекс 11К, где-то в своем собственном стеке, операционная система выполнит хак и на самом деле загрузит индекс памяти 1500, потому что индекс 11K принадлежит задаче B. На самом деле, всё адресное пространство каждой программы в памяти — это просто виртуальная память. Каждая программа, работающая на компьютере, обращается к виртуальным адресам, с помощью некоторых аппаратных чипов ОС будет обманывать процесс, когда он будет пытаться получить доступ к любой зоне памяти.

ОС виртуализирует память и гарантирует, что любая задача не сможет получить доступ к памяти, которой не владеет. Виртуализация памяти позволяет изолировать процесс. Задача А больше не может получить доступ к памяти задачи B и не сможет получить доступ к памяти ОС. И все это полностью прозрачно для задач на уровне пользователя, благодаря тоннам сложного кода ядра ОС. Таким образом, операционная система обслуживает каждый запрос памяти процесса. Это работает очень эффективно — даже если запущено слишком много различных программ. Для достижения этой цели процесс получает помощь от аппаратного обеспечения, главным образом от процессора и некоторых электронных компонентов вокруг него, таким как блок управления памятью (MMU ). MMU появился в начале 70-х годов, вместе с IBM, как отдельные чипы. Сейчас они встраиваются непосредственно в наши чипы CPU и являются обязательными для любой современной ОС для запуска. На самом деле, операционная система не выполняет тонны операций, а полагается на некоторые аппаратные особенности поведения, которые облегчат доступ к памяти.

Вот небольшая программа на C, показывающая некоторые адреса памяти:

#include #include int main (int argc, символ ** argv)

На моей LP64 x86_64 машине он показывает:

Code is at 0x40054c Stack is at 0x7ffe60a1465c Heap is at 0x1ecf010

Мы можем видеть, что адрес стека находится намного выше адреса кучи, а код расположен ниже стека. Но каждый из этих 3-х адресов являются подделками: в физической памяти, по адресу 0x7ffe60a1465c точно не расположено целое число 3. Помните, что каждая программа пользователя манипулирует адресами виртуальной памяти, тогда как программы уровня ядра, таких как само ядро ​​ОС (или аппаратный код драйвера) могут манипулировать физическими адресами RAM.

Трансляция адреса

Адресная трансляция — это формулировка, за которой скрывается некая магическая техника. Аппаратное обеспечение (ММУ)транслирует каждый виртуальный адрес программы уровня пользователя в правильный физический адрес. Таким образом, операционная система помнит для каждой задачи соответствие между его виртуальными адресами и физическими. И это является сложной задачей. Операционная система управляет всей памятью задач на уровне пользователя для каждого требования доступа к памяти, обеспечивая тем самым полную иллюзию процессу. Таким образом, операционная система преобразует всю физическую память в полезную, мощную и простую абстракцию.

Давайте подробно рассмотрим простой сценарий:
Когда процесс запускается, операционная система бронирует фиксированную область физической памяти, скажем, 16KB. Затем сохраняет начальный адрес этого пространства в специальную переменную, называемую базой. Потом устанавливает другую специальную переменную, называемую границами (или пределом) ширины пространства — 16КB. Далее операционная система сохранит эти два значения в таблице процессов, называемой PCB (Process Control Block).
А вот как выглядит процесс виртуального адресного пространства:

image

А вот его физическое изображение:

image

ОС решила сохранить его в физической памяти в диапазоне адресов от 4K до 20K. Таким образом, базовый адрес устанавливается в 4K, а предел установлен на 4 + 16 = 20К. Когда этот процесс планируется (учитывая некоторое время процессора), операционная система считывает обратно предельные значения из PCB и копирует их в конкретные регистры процессора. Когда CPU во время работы попытается загрузить, например, виртуальный адрес 2K (что-то в его куче), CPU добавит этот адрес базы, полученный от операционной системы. Таким образом, процесс доступа памяти приведет к физическому местоположению 2K + 4K = 6К.

Физический адрес = виртуальный адрес + предел

Если полученный физический адрес (6К) находится вне границ (-4K | 20K-), это означает, что процесс попытался получить доступ к неправильному участку памяти, которым он не владеет. Процессор сгенерирует исключение, и поскольку в ОС есть обработчик исключений для этого события, ОС активируется процессором и будет знать, что исключение памяти только что произошло на CPU. Затем ОС по умолчанию передаст сигнал поврежденному процессу “SIGSEGV”. Ошибка сегментации, которая по умолчанию (это может быть изменено) завершит задачу с сообщением — “Произошел сбой в работе с недопустимым доступом к памяти”.

Перемещение памяти

Еще лучше, если задача А не запланирована, это означает, что она извлекается из CPU, так как планировщик попросил запустить другую задачу (скажем, задачу B). При выполнении задачи B ОС может свободно переместить всё физическое адресное пространство задачи A. Операционная система часто получает время процессора при выполнении пользовательских задач. Когда завершается последний системный вызов, управление ЦП возвращается к ОС, и до выполнения системного вызова ОС может делать все, что захочет, управляя памятью, перемещая все пространство процесса в различные физические слоты карт памяти.

Это относительно просто: операционная система перемещает область 16K в другую 16K свободного пространства, и просто обновляет базовые и связанные переменные задачи A. Когда задача будет возобновлена ​​и передана CPU, процесс преобразования адреса по-прежнему будет работать, но это не приведет к тому же физическому адресу, как раньше.

Задача А не заметила ничего, с её точки зрения, её адресное пространство по-прежнему начинается от 0К до 16К. Операционная система и аппаратное обеспечение MMU берут полный контроль над каждым доступом к памяти для задачи А, давая ей полную иллюзию. Программист задачи A манипулирует своими разрешенными виртуальными адресами, от 0 до 16, а MMU будет заботиться о позиционировании в физической памяти.

Образ памяти после перемещения будет выглядеть следующим образом:

image

В настоящее время программист больше не должен задаваться вопросом, где его программа находиться в оперативной памяти, если другая задача работает рядом с его собственной, и какими адресами манипулировать. Это делает сама ОС вместе с очень производительным и умным аппаратным средством — “Блок управления памяти” (MMU).

Сегментация памяти

Обратите внимание на внешний вид слова «сегментация» — мы близки к объяснению почему происходят ошибки сегментации. В предыдущей главе мы объяснили о трансляции и перемещении памяти, но модель, которую мы использовали имеет свои недостатки:
Мы предположили, что каждое виртуальное адресное пространство фиксируется шириной 16Kb. Очевидно, что это не так в действительности. Операционная система должна поддерживать список физической памяти свободных слотов (шириной 16 Kb), чтобы иметь возможность найти место для любого нового процесса с просьбой начать или переместить запущенный процесс. Как сделать это эффективно, чтобы не замедлить всю систему? Как вы можете видеть, каждый процесс занимает участок памяти 16 Kb, даже если процесс не использует всё своё адресное пространство, что очень вероятно. Эта модель явно тратит много памяти, процесс потребляет1KB памяти, а его участок памяти в физической памяти 16Кб. Такие отходы называют внутренней фрагментацией: память зарезервирована, но никогда не используется.

Для решения некоторых из этих проблем, мы собираемся погрузиться в более сложную организацию памяти в ОС — сегментацию. Сегментацию легко понять: мы расширяем идею «базы и границ» трех логических сегментов памяти: кучи, кода и стека; каждого процесса — вместо того, чтобы просто рассматривать образ памяти в качестве одного уникального объекта.

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

image

Теперь легко заметить, что свободное пространство в виртуальной памяти для задач А больше не выделяется в физической памяти, использование памяти становится гораздо более эффективным. Единственным отличием является то, что в настоящее время для любой задачи, ОС должна запоминать не одну пару баз/границ, а три: по одной паре для каждого типа сегмента. ММУ заботится о переводе, так же, как и раньше, и теперь поддерживает, 3 базовых величин, и 3 границ.

Например, здесь, куча задачи A имеет базу 126K и границы 2К. Затем задача просит доступ к виртуальному адресу 3KB, в куче; физический адрес 3Kb — 2Кб (начало кучи) = 1Kb + 126K (смещение) = 127K. 127K находится перед 128К — это правильный адрес памяти, который может быть выполнен.

Совместное использование сегментов

Сегментирование физической памяти не только решает проблему свободной виртуальной памяти, освобождая больше физической памяти, но она также позволяет разделить физические сегменты с помощью различных процессов виртуальных адресных пространств. Если запустить дважды ту же задачу, задачу А например, сегмент кода точно такой же: обе задачи выполняют одни и те же команды процессора. Обе задачи будут использовать свой собственный стек и кучу, обращаться к своим собственным наборам данных. Неэффективно дублировать сегмент кода в памяти. ОС теперь может разделить его и сэкономить еще больше памяти. Например так:

image

На картинке выше, А и В имеют свою собственную область кода в их соответствующем виртуальном пространстве памяти, но под капотом. Операционная система разделяет эту область в одних и тех же физических сегментах памяти. Обе задачи А и В абсолютно не видят этого, для них обеих — они владеют своей памятью. Для достижения этой цели ОС должна реализовать еще одну особенность: бит защиты сегмента. ОС будет для каждого физического сегмента создавать, регистрировать границы / пределы для правильной работы блока MMU, но она также зарегистрирует флаг разрешения.

Поскольку код не является изменяемым, сегменты кода все созданы с флагом разрешения RX. Процесс может загрузить эту область памяти для выполнения, но любой процесс, попытавшийся писать в эту область памяти, будет завершен ОС. Остальные два сегмента: куча и стек являются RW, процессы могут читать и писать от своего собственного стека / кучи, но они не могут выполнять код из него (это предотвращает недостатки безопасности программы, где плохой пользователь может захотеть повредить кучу или стек, чтобы ввести код для запуска, которому будет доступно ядро ОС. Это невозможно, так как куча и стек сегментов часто не являются исполняемыми. Обратите внимание, что это было не всегда, так как требует дополнительной поддержки аппаратного обеспечения для эффективной работы, это называется «бит NX» под процессором Intel). Сегменты памяти разрешений изменяемы во время выполнения: задача может потребовать mprotect () от ОС. Эти сегменты памяти отчетливо видны под Linux, используйте утилиты /proc//maps или /usr/bin/pmap

Ниже приведен пример для PHP:

$ pmap -x 31329 0000000000400000 10300 2004 0 r-x-- php 000000000100e000 832 460 76 rw--- php 00000000010de000 148 72 72 rw--- [ anon ] 000000000197a000 2784 2696 2696 rw--- [ anon ] 00007ff772bc4000 12 12 0 r-x-- libuuid.so.0.0.0 00007ff772bc7000 1020 0 0 ----- libuuid.so.0.0.0 00007ff772cc6000 4 4 4 rw--- libuuid.so.0.0.0 . . 

Здесь мы детально видим все отображения памяти. Адреса являются виртуальными и отображают их разрешения области памяти. Как мы можем видеть, каждый общий объект (.so) отображается в адресное пространство как несколько отображений (вероятно кода и данных), и области кода являются исполнимыми, а под капотом они разделяются в физической памяти между каждым процессом, отображающим такой общий объект в его собственное адресное пространство…

Самое большое преимущество разделяемых объектов под Linux (и Unix) — это экономия памяти. Возможно также создать общую зону, ведущую к общему физическому сегменту памяти, с помощью системного вызова mmap(). Буква ‘s’, появившаяся рядом с этой областью, означает “разделяемая”.

Пределы Сегментации

Мы видели, что сегментация решает проблему не используемой виртуальной памяти. Когда процесс не использует некоторый объем памяти, этот процесс не отображается в физическую память благодаря сегментам, которые соответствуют выбранной памяти. Тем не менее, это не совсем верно. Что делать, если процесс требует 16Kb кучи? ОС, скорее всего, создаст сегмент физической памяти размером 16КБ. Но если пользователь затем освобождает 2Кб такой памяти? Тогда, ОС должна уменьшить сегмент 16Кб до 14kb. Что делать, если теперь программист спрашивает 30Kb кучи? Старый сегмент 14Kb теперь должен вырасти до 30 КБ, но может ли он это сделать? Другие сегменты теперь могут окружать наш сегмент 14kb, препятствуя его росту. Тогда ОС придется искать свободное пространство 30 КБ, а затем перемещать сегмент.

image

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

Иногда, ОС путем суммирования всех свободных сегментов получает некоторое доступное пространство, но так как оно не соприкасается, она не может использовать это пространство и должна отказать требованию памяти от процессов, даже если существует пространство в физической памяти. Это очень плохой сценарий. Операционная система может попытаться сжать память путем слияния всех свободных участков в один большой кусок, который можно было бы использовать в будущем, чтобы удовлетворить запрос памяти.

image

Но такой алгоритм уплотнения сложен для CPU, и в это время ни один пользовательский процесс не сможет получить ​​CPU: операционная система полностью загружена реорганизацией своей физической памяти, таким образом, система становится непригодной для использования. Сегментация памяти решает множество проблем управления памятью и многозадачности, но она также показывают реальные недостатки. Таким образом, существует необходимость в расширении возможностей сегментации и исправлении этих недостатков. Отсюда образовалось ещё одно понятие — “пагинация памяти”.

Пагинация памяти

Пагинация страниц памяти разделяет некоторые концепции сегментации памяти и пытается решить её проблемы. Мы увидели, что основной проблемой сегментации памяти является то, что сегменты будут увеличиваться и уменьшаться очень часто, так как пользовательский процесс запрашивает и освобождает память. Иногда операционная система сталкивается с проблемой. Она не может найти большую область свободного места, чтобы удовлетворить запрос памяти пользовательского процесса, потому что физическая память стала сильно фрагментирована с течением времени: она содержит много сегментов разного размера по всей физической памяти, что приводит к сильно фрагментированной физической памяти.

Разбивка решает эту проблему с помощью простой концепции: что если для каждого физического распределения ядро ​​будет выделять фиксированный размер? Страницы — это сегменты физической памяти фиксированного размера. Если операционная система использует распределения фиксированного размера, то намного легче управлять пространством, что в итоге сводит на нет фрагментацию памяти.
Давайте покажем пример еще раз, предполагая небольшое 16Кб виртуальное адресное пространство, чтобы облегчить представление:

image

С пагинацией мы не говорим о кучи, стеке или сегменте кода. Мы разделим весь процесс виртуальной памяти на зоны фиксированного размера: мы называем их страницы. На приведенном выше примере мы разделили адресное пространство в виде 4-х страниц, 4КБ каждый.

Затем мы делаем то же самое с физической памятью.

image

И операционная система просто хранит то, что называется «Таблица процессорных страниц» — связь между одной страницей виртуальной памяти процесса и базовой физической страницей (который мы называем «страница кадра»).

image

С помощью этого способа управления памятью больше нет проблем в свободном управлении пространством. Кадр страницы отображается (используется) или нет, ядру легко найти достаточное количество страниц, чтобы удовлетворить запрос памяти пользовательского процесса. Он просто хранит список свободных страниц кадров, а также просматривает его при каждом запросе памяти. Страница является наименьшей единицей памяти, которой ОС может управлять. Страница (на нашем уровне) является неделимой. Вместе со страницами каждому процессу прилагается таблица страниц, которая хранит адресные переводы. Переводы больше не используют значения границы, как это было с сегментацией, а использует «номер виртуальной страницы (VPN)» и «смещение» на этой странице.

Давайте покажем пример преобразования адресов с нумерацией страниц. Виртуальное адресное пространство размером 16Kb, т.е. нам нужно 14 бит для представления адреса (2 ^ 14 = 16Kb). Размер страницы 4 Кб, так что нам нужно 4kb (16/4), чтобы выбрать нашу страницу:

image

Теперь, когда процесс хочет загрузить, например, адрес 9438 (из 16384 возможностей), это дает 10.0100.1101.1110 в двоичной системе, что приводит к следующему:

image

То есть 1246й байт виртуальной страницы 2 ( «0100.1101.1110» го байта в «10» й странице). Теперь, операционная система должна искать эту страницу таблицы процессов, чтобы узнать на какой странице 2я карта. Согласно тому, что мы предложили, страница 2 — это 8К байт в физической памяти. Таким образом, виртуальный адрес 9438 приводит к физическому адресу 9442 (8k + 1246 смещение). Мы нашли наши данные! Как мы уже говорили, существует лишь одна таблица страниц для каждого процесса, поскольку каждый процесс будет иметь свои собственные переводы адреса, так же, как с сегментами. Но подождите, где эти таблицы страниц хранятся на самом деле? Угадайте . в физической памяти, да, а где же ей ещё быть? Если таблицы страниц сами хранятся в памяти, следовательно, для каждого доступа к памяти, память должна иметь доступ для извлечения VPN. Таким образом, со страницами один доступ к памяти равен фактически двум доступам к памяти: один для извлечения записи таблицы страниц, и один, чтобы получить доступ к «реальным» данным. Зная, что доступ к памяти медленный процесс, получаем — что это не лучшая идея.

Переводно-ассоциативный буфер: TLB

Использование поискового вызова в качестве механизма ядра для поддержки виртуальной памяти может привести к высокой производительности накладных расходов. Измельчение адресного пространства на небольшие фиксированного размера единицы (страницы) требует большого количества информации о карте. Потому что информация о карте, как правило, хранится в физической памяти. Логический поиск подкачки требует дополнительного поиска в памяти для каждого виртуального адреса, сгенерированного программой. И тут опять приходят аппаратные средства, чтобы ускорить и помочь ОС. В пагинации, как и в сегментации, аппаратные средства помогают ядру ​​ОС перевести адреса эффективным приемлемым способом. TLB является частью MMU, всего лишь простой кэш некоторых VPN переводов. TLB будет предотвращать доступ ОС к памяти от доступа к таблице страниц процесса, для получения физического адреса памяти из виртуальной. Аппаратное обеспечение MMU будет срабатывать на каждом виртуальном доступе к памяти, извлекая VPN из этого адреса, и выполняя поиск TLB, если он совпадает для этого конкретного VPN. Если он совпал, то он просто выполнил свою роль. Если он получает промах, то он будет искать таблицу страниц процесса, и если ссылка на память действительна, она обновит TLB, так что любой дальнейший доступ памяти этой страницы попадет в TLB.

Как и в любом кэше, вы чаще попадете, чем получите промах, так как ситуация промах вызывает страницу поиска, вследствие чего, доступ к памяти будет медленным. Вы догадаетесь об этом. Чем больше страниц, тем лучше TLB попадает, но тем больше неиспользуемого пространства вы будете иметь на каждой странице. Здесь должен быть найден компромисс. Современные ядра используют различные размеры страницы. Ядро ​​Linux может использовать то, что называется «огромные страницы», страницы, размером 2Mb что больше традиционных 4Kb. Кроме того, информацию лучше хранить в смежных адресах памяти. Если вы сократите свои данные в памяти, то скорее всего будете страдать от пропусков TLB или от переполнения TLB. Это то, что называется Эффективность пространственной локальности TLB: Данные, находящиеся непосредственно рядом с вами, могут быть сохранены в той же физической странице, таким образом, получая выгоду из текущего доступа к памяти из TLB.

Для переключения контекста TLB хранит также то, что называется ASID в каждой из своих записей. Это идентификатор адресного пространства, который является чем-то вроде PID. Каждый запланированный процесс имеет свой собственный ASID, следовательно TLB может управлять любым адресом процесса без риска недействительных адресов из других процессов. Также, если пользовательский процесс попытается загрузить неверный адрес, адрес, скорее всего, не будет сохранен в TLB, следовательно произойдет промах в поиске в записи таблицы страниц процесса. Теперь адрес сохраниться, но с неверным битом. Под X86 каждый перевод использует 4 байта, таким образом, имеется много бит. И нередко можно найти действительный бит вместе с другими составными, такими как грязный бит, биты защиты, опорного бита и т.д.… Если запись помечена как недопустимая, ОС по умолчанию использует SIGSEGV, что приводит к «ошибке сегментации», даже если здесь мы больше не говорим о сегментах. Вы должны знать, что подкачка более сложна в современных ОС, чем я объяснил. Современные ОС, как правило, используют многоуровневые таблицы страниц, многостраничные размеры, а также важную концепцию, которую мы не будем объяснять. Страница выселения, известная как «swaping» (Процесс, когда ядро обменивает страницы памяти на дисковое пространство, чтобы эффективно использовать главную память и создать иллюзию для пользовательских процессов, что основная память не ограничена в пространстве).

Вывод

Теперь вы знаете, что находится под сообщением «Ошибка сегментации». Операционная система использует сегменты для отображения виртуальной памяти в физической памяти. Когда процесс ядра хочет получить доступ к некоторой памяти, он выдает требование о том, чтобы MMU переводил на физический адрес памяти. Но если этот адрес не правильный, из пределов физического сегмента, или если права защиты сегмента не подходящие, то ОС по умолчанию посылает сигнал процессу, вызвавшего отказ: SIGSEGV, который имеет обработчик по умолчанию. Он убивает процесс и выводит сообщение: «Ошибка сегментации”. В других операционных системах (предполагаю) часто сообщение выглядит как „Общая неисправность защиты“. С Linux — нам повезло, мы имеем возможность доступа к исходному коду, здесь есть место исходного кода для X86 / 64 платформ, которое управляет ошибками доступа к памяти, так же здесь и SIGSEGV. Если вы заинтересованы в разработке сегментов для X86 / 64 платформ, вы можете посмотреть на их определение в ядре Linux. Вы также найдете интересный материал о пейджинговой памяти, которая поддерживает более длинный путь сегментирования памяти, чем использование классических сегментов. Мне понравилось писать эту статью, она перенесла меня в конец девяностых, когда я программировал свой первый CPU: Motorola 68HC11 с использованием C, VHDL и прямого монтажа. Я не программировал виртуальное управления памятью ОС, а использовал физические адреса напрямую (моему процессору не нужны такие сложные механизмы). Затем я подался в Web; но мои первые знания пришли от электроники, систем которые мы используем каждый день…

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *