Лекция 05 - chrislvt/OS GitHub Wiki

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

1

С этой точки зрения в системе существует две задачи управления

  • вертикального управления - задача передачи информации с одного уровня на другой.
  • горизонтальное управление - управление каждым конкретным уровнем иерархии памяти.

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

Архаичные схемы управления памятью

Когда мы рассматривали стихию ОС, мы говори об однопрограммных системах пакетной обработки, когда в памяти компьютера находилась одна программа, все ресурсы этой системы были в распоряжении одной единственной программы. Потом появились многопрограммные системы пакетной обработки.

Сначала мы проведем классификацию схем управления памятью, как еще говорят - распределения памяти. Cуществует два способа распределения памяти:

  1. связанное распределение
  2. несвязанное распределение

Cвязанное распределение предполагает, что программа располагается в памяти целиком, в непрерывной последовательности отрисовки.

Несвязанное распределение предполагает, что программа находится в памяти кусками.

2

3

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

DOS был написан, когда вся теория ОС уже существовала, но при создании DOS учитывались ограничения, в частности объем памяти - 1МБ. Поэтому DOS писался именно с учетом ограничения объема памяти, но это действительно однопрограммная ОС, но даже в ней можно только условно говорить об одиночном непрерывном распределении. Потому что память, на самом деле, выделялась сегментам программам, а в простойной программе было три сегмента(кода, данных, стека). Но это однопрограммная ОС. Здесь всплывает понятие, которое сейчас уходит, но оно не становится менее важным - резидентная часть.

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

В современных системах ОС делиться также на две части: paging, non paging.

Многозадачность - когда в памяти одновременно большое количество программ. Самыми первыми схемами были интуитивно понятными. Самое простое решение - поделить память на разделы фиксированного размера.

Распределение памяти разделами

Статическое распределение - поделить память на разделы, причем размеры раздела определить до начала работы ОС, при выборе размера разделов, учитывалась статистика программ разных резделов. Скажем один раздел маленький, средний и большой. Могло быть несколько маленьких, пять средних итд. Каждому разделу устанавливалась очередь. Очевидно, что больших программ у нас мало. И у нас очередь из маленьких программ, средних и получается, что этот огромные раздел не используется. Решение - сделать одну очередь. Но здесь надо выбрить раздел, в который мы можем загрузить программу. Здесь возникает задача - выбирать раздел для загрузки и возникает все та же проблема, если мы поместили маленькую программу в большой раздел - неэффективное распределение памяти. Тогда на смену статическому распределению пришло динамическое.

4

6

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

7

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

1 стратегия называется первой подходящий - программа загружается в первый подходящий раздел.

2 стратегия называется самый тесный - программа будет загружена в раздел, который ближе всего к ее размеру.

3 стратегия называется самый широкий - потому что в этом разделе останется достаточно места для загрузки еще одной программы.

8

Далее выбрали мы раздел и в освободившийся раздел мы загрузили какую-то программу и у нас все равно в этом разделе останется не занятое адресное пространство. Пусть завершилась 3 программа, здесь возникает задача объединения освободившихся соседних участком памяти, которая должна решаться при управлении памяти. Самый простой способ управления - использование соответствующих таблиц - лучше использовать связанные списки, которые можно редактировать, соответсвенно мы рассмотрим табличный способ, который более простой. Для такого управления памятью нужно, как минимум 2 таблицы:

Таблицы выделенных разделов

9

Таблица свободных областей или разделов

10

Фрагментация памяти - в результате постоянной загрузки и выгрузки программы возникновение очень небольших участков памяти (свободных), в которые не может быть загружена ни одна программа. В итоге для такой фрагментации было характерно не использование довольно больших объемов памяти т.е невозможно было ничего сделать с этими небольшими участками и буквально до 30% памяти могло уходить на фрагментацию. C ней по-разному боролись, но не будут рассказывать, так как неактуально. Но само слово фрагментация надо знать. Речь идет о неперемещаемых разделов. Или о неперемещаемых программах. Т.е о программах, которые привязаны к конкретным адресам. Мы с вами говорили об абсолютных адресах - конкретные адреса физической памяти, которые непосредственно в программе программистом. Очевидно, что с появлением языков высокого уровня и понятием “переменная” позволило оперировать этими понятиями, но программы траслировались и загружались в определенные участки памяти т.е связывались с определенными адресами однозначно. Очевидно, что такой подход тормозил развитие и программного обеспечения и использование вычислительных систем.

Вот мы поняли, что у нас большой объем памяти не используется из-за фрагментации. И какой же выход? Существует один единственный путь: перезагрузка системы.

Чем чревато перезагрузка системы? Потерей проделанной работы. Какими-то программами, которые были незавершенны. Первый способ, который стали применять: Перемещение разделов

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

image

На экзамене потребуют все цифры. 352к, 376к - на разных машинах для обозначения команд использовались разные слова. 1,370,248

Вот мы переместили к вверху нашей программу с помощью регистра перемещений(аппаратная поддержка). Даже такой регистр можно было бы выделить в оперативной памяти. В режиме long такие регистры есть, они называются архитектурно зависимые регистры. Новые решения базируются на старых. Здесь очень важные мысли присутствуют, а именно: мы видим, что для перемещения нашей программы(адреса мы не меняем) у нас пока что абсолютные адреса. Но мы научились перемещать - мы сделали регистр перемещений, а что это потребовало? Это потребовало - преобразования адреса. Т.е простая возможность перемещения программы привела к необходимости к преобразования адресов. Но как только они это сделали, кого-то осенила совершенно гениальная мысль! А чего связывать программу с конкретными адресами? Пусть каждая программа считает, что она начинается с нулевого адреса. Т.е ввели понятие логических адресов, которая выражается вот этой мыслей - любая программа считает, что она начинается с нулевого адреса.

[Молчание] Эта мысль родилась из такого простого соображения, что начальный адрес адресного пространства программы фактически не связан с физическим расположением программы в памяти. В результате данное решение преобразутеся в следующее:

image

В процессоре, скажем, тот же регистр перемещений, но так же введен регистр границ, пауза исполнительный адрес 9800. В итоге: в программе, как вы видите, у нас находятся смещения

Отчитывает студентов

Посмотрите, какая важная мысль: теперь в программе находятся смещения. Преобразование осталось, оно тоже существует. Но к начальному адресу мы прибавляем смещение - получаем линейный физический адрес. К начальному адреса раздела прибавляем смещение, получаем линейный физический адрес. И как вы видите - не сразу пришли, к тому, чем мы пользуемся не задумываясь. То есть, для того, чтобы иметь возможность перемещать программу, программировать в относительных адресах, были изменены основные понятия. В итоге, естественно, поменялось программное обеспечение, связанное с так называемыми системами программирования: с трансляцией и загрузкой программ в память. Но возникает вопрос: да, был сделан такой интеллектуальный прорыв, такие достижения, но все-равно возникает вопрос: а КОГДА перемещать программы? Перемещение программы - затратное действие, когда мы загрузили программу, у нее определеный начальный адрес. Очевидно, что если мы переместим программу, мы должны будем выполнить какие-то дополнительные действия, дополнительные накладные расходы. Когда выполнять перемещение программы? Два варианта: Первый вариант

Осуществлять такое перемещение программ в памяти, как только освободился какой-либо раздел ("подтягивать программы в освободившиеся адреса").

Второй вариант

Выполнять такое перемещение, если не найден свободный раздел нужного размера.

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

  1. ОС и так называемые утилиты
  2. Системы программирования (трансляторы, редакторы связей, загрузчики)

Мы с вами рассмотрели связанное распеределние, которое предполагает, что программа располагается в памяти целиком, в непрерывных адресах. Даже несмотря на возможность перемещения, мы с вами видим, что это связано с определенными затратами. А нет ли никакого более эффективного способа распределения памяти? Несвязанное распределение. Как ни странно, несмотря на то, что программы делятся на модули, были так называемые оверлеи (не уверен, что это слово). Сначала возникла мысль: "А если мы поделим программу на участки равного размера, назовем их страницами (page). И память мы поделим на такие же участки. У нас будут таблицы, которые описывают адресное пр-во программы, адресное пр-во памяти физическое, тогда мы вот эти страницы программы можем загружать в любые свободные страницы памяти, причем они страницы памяти стали называть фреймами (рамками)"

image

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

При такой организации можем загружать страницы в любые свободные страницы памяти. Для этого чтобы загрузить программу нам нужно иметь соответствующее количество свободных страниц. Программа, все её страницы загружались в память. Это потребовало введения таблиц страниц соответственно в этой таблице страниц указывался физический адрес страницы в которая была загружена как бы логическая страница. Преобразование адреса так же выполнялось, но уже с точки зрения страниц. То же самое можно сказать относительно сегментов. Программу можно поделить на сегменты (на логичекие единицы деления программы). Тогда в каждый отдельный сегмент может загружаться соответсвтующий раздел памяти. Но сразу возник вопрос, а нужно ли загружать в память все страницы программы? Все эти рассуждения характерны для третьего поколения машин ( IBM 360), это уже мощнейшая машина, мощнейший мейнфрейм. Возникает вопрос, а надо ли загружать все страницы программы? Тем более что некоторые страницы могут вообще не использоваться, программа ведь выполняется по разным траекториям, при каких-то части программы могут вообще в данном прогоне не работать. Если сформулировать вопрос по-другому: можно ли выполнить программу которая целиком не находится в памяти?(В архитектуре Фон Неймана) Можно, но в памяти должны находиться части кода к которым в данный момент обращается процессор. Если оперировать понятием страница, то в памяти должны находиться страницы с которыми в текущий момент работает процессор. Эта идея воплотилась в понятие виртуальная память.

Виртуальная память

Виртуальная - каждущаяся, возможная, но реально не существующая.

Существует 3 схемы управления виртуальной памятью:

  1. Управление памятью страницами по запросу

  2. Управление памятью сегментами по запросу

  3. Управление памятью сегментами поделёнными на страницы по запросу.

Во всех названиях существует словосочетание "по запросу". Т.е. соответствие части кода загружаются в память по запросу когда процессор обращается к этим частям кода. Очевидно что если они в памяти отсутствуют, в этом случае возникнет соответствующее исключение. Например 14-е исключение PageFault. В результате обработки этого исключения, отсутствующая в памяти страница будет в неё загружена.

Управление памятью страницами по запросу

В отличие от предыдущего рассуждения, когда мы с вами логическим путём дошли до понятия любая программа считает, что она начинается с нулевого адреса, т.е. до понятия логического адресного пространства, мы видим, что появляется ещё один тип адресног пространства - виртуальное адресное пространство. Программа как бы располагается в виртуальном адресном пространстве, ну мы с вами очень плотно изучаем 32х разрядные машины. Для защищённого режима такое виртуальное адресное пространство процесса равно 4Гб. В этом адресном пространстве располагается(отображается) адресное пространство программы на определённые адреса виртуального адресного пространства. И туда же отображается операционная система. Более подробно об этом на семинаре.

Что такое виртуальное адресное пространство? Это виртуальное адресное пространство которым оперирует система.