Студопедия.Орг Главная | Случайная страница | Контакты | Мы поможем в написании вашей работы!  
 

ВВЕДЕНИЕ 10 страница



Рис. 3.16. Master Boot Record и таблица разделов

Разбиение физического диска на логические программа MBR осуществляет на основе содержащейся в ее теле таблицы разделов (partition table), которая содержит границы и типы разделов. MBR перехватывает прерывание int 13^ и транслирует обращения к дисковой подсистеме так, что обращения к логическому диску N преобразуются в обращения к N-ному разделу физического диска
Один из разделов диска должен быть помечен как активный или загрузочный. MBR загружает начальный сектор этого раздела — обычно это и есть первичный загру3. чик ОС. Многие реализации MBR, в том числе и поставляемая с Solaris, могут предоставлять пользователю выбор раздела, с которого следует начинать загрузку Выбор обычно предоставляется в форме паузы, в течение которой пользователь может нажать какую-то клавишу или комбинацию клавиш. Если ничего не будет нажато, начнется загрузка с текущего активного раздела.
Так или иначе, но загрузочный сектор — по совместительству, первичный загрузчик Solaris оказывается в памяти и начинает исполняться. Исполнение его состоит в том, что он загружает — нет, еще не ядро, а специальную программу, называемую DCU (Device Configuration Utility, утилита конфигурации устройств). Основное назначение этой программы — имитация сервисов консольного монитора компьютеров фирмы Sun на основе процессоров SPARC.
DCU производит идентификацию установленного в машине оборудования. Пользователь может вмешаться в этот процесс и, например, указать системе, что такого-то устройства в конфигурации нет, даже если физически оно и присутствует, или установить драйверы для нового типа устройств. Драйверы, используемые DCU, отличаются от драйверов, используемых самим Solaris, называются они BEF (Boot Executable File), и начинают исполнение, как и сама DCU, в.реальном режиме процессора х86..
Найдя все необходимое оборудование, DCU запускает вторичный загрузчик Solaris. Логический диск, выделенный Solaris, имеет внутреннюю структуру и также разбит на несколько разделов (рис. 3.17). Чтобы не путать эти разделы с разделами, создаваемыми MBR, их называют слайсами (slice). Загрузочный диск Solaris должен иметь минимум два слайса — Root (корневая файловая система) и Boot, в котором и размещаются вторичный загрузчик и DCU.
Вторичный загрузчик, пользуясь BEF-модулем загрузочного диска для доступа к этому диску, считывает таблицу слайсов и находит корневую файловую систему. В этой файловой системе он выбирает файл /kernel/unix, который и является ядром Solaris. В действительности, вторичный загрузчик исполняет командный файл, в котором могут присутствовать условные операторы, и, в зависимости от тех или иных условий, в качестве ядра могут быть использованы различные файлы, /kernel/unix используется по умолчанию.
Кроме того, пользователю предоставляется пауза (по умолчанию 5 секунд), в течение которой он может прервать загрузку по умолчанию и приказать загрузить какой-то другой файл, или тот же файл, но с другими параметрами.
Будучи так или иначе загружено, ядро, пользуясь сервисами вторичного загрузчика, считывает файл /etc/system, в котором указаны параметры настройки системы. Затем, пользуясь информацией, предоставленной DCU, ядро формирует дерево устройств — список установленного в системе оборудования, и в соответствии с этим списком начинает подгружать модули, управляющие устройствами— драйверы. Подгрузка по-прежнему происходит посредством сервисов вторичного загрузчика — ведь все драйверы размещены на загрузочном диске и в корневой файловой системе, в том числе и драйверы самого этого диска и этой файловой системы.

Рис. 3.17. Структура раздела Solaris

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

Существуют ОС, которые не умеют самостоятельно выполнять весь цикл бутстрапа. Они используют более примитивную операционную систему, которая исполняет их вторичный (или какой это уже будет по счету) загрузчик, и помогает этому загрузчику поместить в память ядро ОС. На процессорах х8б в качестве стартовой системы часто используется MS/DR DOS, а загрузчик новой ОС оформляется в виде ЕХЕ-файла.
Таким образом устроены системы MS Windows l.x-З.х, Windows 95/98/ME, DesqView и ряд других "многозадачников" для MS DOS. Таким же образом загружается сервер Nowell Netware, система Oberon для х86, программы, написанные для различных расширителей DOS (DOS extenders) и т. д. Многие Из перечисленных систем, например Windows (версии младше З.11 — в обязательном порядке, а З.11 и 95/98/МЕ только в определенных конфигурациях) используют DOS и во время работы в качестве дисковой подсистемы. Тем не менее, эти программные пакеты умеют самостоятельно загружать Пользовательские программы и выполнять все перечисленные во введении Функции и должны, в соответствии с нашим определением, считаться полноценными операционными системами.

Глава 4. Управление оперативной памятью

Управление оперативной памятью

Управление оперативной памятью

Основной ресурс системы, распределением которого занимается ОС — это оперативная память. Поэтому организация памяти оказывает большое влияние на структуру и возможности ОС. В настоящее время сложилась даже более интересная ситsssуация — переносимая операционная система UNIX, рассчитанная на машины со страничным диспетчером памяти, произвела жесткий отбор, и теперь практически все машины общего назначения, начиная от х86 и заканчивая суперкомпьютерами или, скажем процессором Alpha, имеют именно такую организацию адресного пространства.

Открытая память

Самый простой вариант управления памятью — отсутствие диспетчера памяти и возможность загружать в системе только один процесс. Именно так работают СР/М и RT-ll SJ (Single-Job, однозадачная). В этих системах программы загружаются с фиксированного адреса PROG_START. В СР/М это ОхЮО; в RT-ll — 01000. По адресам от 0 до начала программы находятся векторы прерываний, а в RT-11 — также и стек программы. Операционная система размещается в старших адресах памяти. Адрес SYS_START, с которого она начинается, зависит от количества памяти у машины и от конфигурации ОС (рис. 4.1).

Рис. 4.1. Управление памятью в однопроцессной ОС с открытой памятью

В этом случае управление памятью со стороны системы состоит в том, что загрузчик проверяет, поместится ли загружаемый модуль в пространство от
PROG_START до SYS_START. Если объем памяти, который использует программа, не будет меняться во время ее исполнения, то на этом все управление и заканчивается.
Однако программа может использовать динамическое управление памятью, например функцию malloco или что-то в этом роде. В этом случае уже код maiioco должен следить за тем, чтобы не залезть в системные адреса. Как правило, динамическая память начинает размещаться с адреса PROG_END = PROG_START + PROG_SIZE. FROG_SIZE в данном случае обозначает полный размер программы, т. е. размер ее кода, статических данных и области, выделенной под стек.
Функция malloc () поддерживает некоторую структуру данных, следящую за тем, какие блоки памяти из уже выделенных были освобождены. При каждом новом запросе она сначала ищет блок подходящего размера в своей структуре данных и, только когда этот поиск завершится неудачей, просит новый участок памяти у системы. Для этого используется переменная, которая в библиотеке языка С называется brk_addr (рис. 4.2, пример 4.1).

Рис. 4.2. Загруженная программа и brk_addr

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

Пример 4.1. Выделение дополнительной памяти в GNU LibC для Linux

morecore.с:
/* Copyright (С) 1991, 1992 Free Software Foundation, Inc.
Этот файл является частью библиотеки С проекта GNU (GNU С Library).
GNU С Library является свободным программным обеспечением;
вы можете передавать ее и/или модифицировать ее в соответствии
с положениями GNU General Public License версии 2 или (по вашему выбору) '
любой более поздней версии.
Библиотека GNU С распространяется в надежде, что она будет полезна, но БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ; даже без неявно предполагаемых гарантий
КОММЕРЧЕСКОЙ ЦЕННОСТИ или ПРИГОДНОСТИ ДЛЯ КОНКРЕТНОЙ ЦЕЛИ. Подробнее см. GNU General Public License.
Вы должны были получить копию GNU General Public License вместе с
GNU С Library; см. файл COPYING. Если вы ее не получили, напишите по
адресу: Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
tifndef _MALLOC_INTERNAL tfdefine _MALLOC_INTERNAL tinclude <malloc.h> #include <stdlib.h> ttinclude <stdio.h>
#include <errno.h> tinclude <sys/syscall.h> tendif
#ifndef _GNU_LIBRARY_
tfdefine __sbrk sbrk
#endif
extern void * ___brk_addr;
extern ptr t sbrk P ((int increment));
int _ initbrk Р ((void));
fifndef NULL fdefine NULL 0 fendif
/* Выделить еще INCREMENT байтов пространства данных
и возвратить начало пространства данных или NULL при ошибках. Если INCREMENT отрицателен, сжать пространство данных. */
__ptr_t
default_morecore (ptrdiff_t increment)
{
ptr_t result = _ sbrk ((int) increment);
if (result == (_ ptr_t) -1)
return NULL; return result;
/* Эта функция почти полностью аналогична _ def ault_morecore () * Но она вызывается только однажды через morecore.
*/
_ ptr_t
_ default_morecore_init (ptrdiff_t increment)
{
_ ptr_t result;
if (_ init_brk()!= 0)
return NULL;
if (_ morecore == _ default_morecore_init)
_mprecore = _ default_morecore;
result = _ sbrk ((int) increment);
if (result == (_ ptr_t) -1)
return NULL;
return result;
}

Алгоритмы динамического управления памятью

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

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

Рис. 4.3. Внешняя фрагментация

В стандартных библиотечных функциях языков высокого уровня, таких как
malloc/free/realloc в С, new/dispose в Pascal и т. д., как правило, используются алгоритмы, рассчитанные на наиболее общий случай: программа запрашивает блоки случайного размера в случайном порядке и освобождает их также случайным образом.
Впрочем, случайные запросы — далеко не худший вариант. Даже не зная деталей стратегии управления кучей, довольно легко построить программу, которая "испортит жизнь" многим распространенным алгоритмам (пример 4.2).





Дата публикования: 2014-11-18; Прочитано: 299 | Нарушение авторского права страницы | Мы поможем в написании вашей работы!



studopedia.org - Студопедия.Орг - 2014-2024 год. Студопедия не является автором материалов, которые размещены. Но предоставляет возможность бесплатного использования (0.007 с)...