РАСПРЕДЕЛЕНИЕ ПАМЯТИ.

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

Когда программа запускается на выполнение, ядро выделяет для нее место в оперативной памяти, при этом совпадение виртуальных адресов, сгенерированных компилято­ром, с физическими адресами совсем необязательно. Ядро, взаимодействуя с аппаратными средствами, транслирует виртуальные адреса в физические, т.е. отображает адреса, сгене­рированные компилятором, в физические, машинные адреса. Такое отображение опирает­ся на возможности аппаратных средств, поэтому компоненты системы UNIX, занимаю­щиеся им, являются машинно-зависимыми. Например, отдельные вычислительные машины имеют специальное оборудование для подкачки выгруженных страниц памяти.

АРХИТЕКТУРА ОПЕРАЦИОННОЙ СИСТЕМЫ UNIX.

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

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

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

На рисунке совокупность обращений к операционной системе разделена на те обращения, которые взаимодействуют с подсистемой управления файлами, и те, которые взаимодействуют с подсистемой управления процессами. Файловая подсистема управляет файлами, размещает записи файлов, управляет свободным пространством, доступом к файлам и поиском данных для пользователей. Процессы взаимодействуют с подсистемой управления файлами, используя при этом совокупность специальных обращений к операционной системе, таких как open (для того, чтобы открыть файл на чтение или запись), close, read, write, stat(запросить атрибуты файла), chown(изменить запись с информаци­ей о владельце файла) и chmod(изменить права доступа к файлу). Подсистема управления файлами обращается к данным, которые хранятся в файле, используя буферный механизм, управляющий потоком данных между ядром и устройствами внешней памяти. Буферный механизм, взаимодействуя с драйверами устройств ввода-вывода блоками, инициирует передачу данных к ядру и обратно. Драйверы устройств являются такими модулями в со­ставе ядра, которые управляют работой периферийных устройств. Устройства ввода-вывода блоками относятся к типу запоминающих устройств с произвольной выборкой; их драйверы построены таким образом, что все остальные компоненты системы восприни­мают эти устройства как запоминающие устройства с произвольной выборкой. Например, драйвер запоминающего устройства на магнитной ленте позволяет ядру системы воспри­нимать это устройство как запоминающее устройство с произвольной выборкой. Подсис­тема управления файлами также непосредственно взаимодействует с драйверами уст­ройств "неструктурированного" ввода-вывода, без вмешательства буферного механизма. К устройствам неструктурированного ввода-вывода, иногда именуемым устройствами посимвольного ввода-вывода (текстовыми), относятся устройства, отличные от устройств ввода-вывода блоками.

Рис. 58. Блок-схема ядра операционной системы

 

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

Примерами обращений к операционной системе, используемых при управлении про­цессами, могут служить fork(создание нового процесса), exec(наложение образа про­граммы на выполняемый процесс), exit(завершение выполнения процесса), wait(синхро­низация продолжения выполнения основного процесса с моментом выхода из порожденного процесса), brk (управление размером памяти, выделенной процессу) и signal(управление реакцией процесса на возникновение экстраординарных событий).

Модуль распределения памяти контролирует выделение памяти процессам. Если в какой-то момент система испытывает недостаток в физической памяти для запуска всех процессов, ядро пересылает процессы между основной и внешней памятью с тем, чтобы все процессы имели возможность выполняться. Есть два способа управления распределением памяти: выгрузка (подкачка) и замещение страниц. Программу подкачки иногда на­зывают планировщиком, т.к. она "планирует" выделение памяти процессам и оказывает влияние на работу планировщика центрального процессора. Однако в дальнейшем мы будем стараться ссылаться на нее как на "программу подкачки", чтобы избежать путаницы с планировщиком центрального процессора.

Модуль "планировщик" распределяет между процессами время центрального процессора. Он планирует очередность выполнения процессов до тех пор, пока они добровольно не освободят центральный процессор, дождавшись выделения какого-либо ресурса, или до тех пор, пока ядро системы не выгрузит их после того, как их время выполнения пре­высит заранее определенный квант времени. Планировщик выбирает на выполнение гото­вый к запуску процесс с наивысшим приоритетом; выполнение предыдущего процесса (приостановленного) будет продолжено тогда, когда его приоритет будет наивысшим сре­ди приоритетов всех готовых к запуску процессов. Существует несколько форм взаимодействия процессов между собой от асинхронного обмена сигналами о событиях до синхронного обмена сообщениями.

Наконец, аппаратный контроль отвечает за обработку прерываний и за связь с маши­ной. Такие устройства, как диски и терминалы, могут прерывать работу центрального процессора во время выполнения процесса. При этом ядро системы после обработки пре­рывания может возобновить выполнение прерванного процесса. Прерывания обрабаты­ваются не самими процессами, а специальными функциями ядра системы, перечисленны­ми в контексте выполняемого процесса. Модуль распределения памяти контролирует выделение памяти процессам. Если в какой-то момент система испытывает недостаток в физической памяти для запуска всех процессов, ядро пересылает процессы между основной и внешней памятью с тем, чтобы все процессы имели возможность выполняться. Есть два способа управления распределением памяти: выгрузка (подкачка) и замещение страниц. Программу подкачки иногда на­зывают планировщиком, т.к. она "планирует" выделение памяти процессам и оказывает влияние на работу планировщика центрального процессора. Однако в дальнейшем мы будем стараться ссылаться на нее как на "программу подкачки", чтобы избежать путаницы с планировщиком центрального процессора.

Модуль "планировщик" распределяет между процессами время центрального процессора. Он планирует очередность выполнения процессов до тех пор, пока они добровольно не освободят центральный процессор, дождавшись выделения какого-либо ресурса, или до тех пор, пока ядро системы не выгрузит их после того, как их время выполнения пре­высит заранее определенный квант времени. Планировщик выбирает на выполнение гото­вый к запуску процесс с наивысшим приоритетом; выполнение предыдущего процесса (приостановленного) будет продолжено тогда, когда его приоритет будет наивысшим сре­ди приоритетов всех готовых к запуску процессов. Существует несколько форм взаимодействия процессов между собой от асинхронного обмена сигналами о событиях до синхронного обмена сообщениями.

Наконец, аппаратный контроль отвечает за обработку прерываний и за связь с маши­ной. Такие устройства, как диски и терминалы, могут прерывать работу центрального процессора во время выполнения процесса. При этом ядро системы после обработки пре­рывания может возобновить выполнение прерванного процесса. Прерывания обрабаты­ваются не самими процессами, а специальными функциями ядра системы, перечисленны­ми в контексте выполняемого процесса.

 

 

ПРОЦЕССЫ

 

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

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

Более современный термин "многозадачность" включает в себя понятие мультипрограммирования, но расширяет эту концепцию и предполагает возможность одновременно­го выполнения отдельных частей одной и той же программы (которые называются нитями или потоками). Большинство программ можно интерпретировать как последовательность более мелких элементов, которые называются задачами. Иногда такие задачи должны вы­полняться синхронно, то есть выполняться и завершаться поочередно и по одной, так, например, для выполнения задачи 2 могут потребоваться результаты задачи 1. В других слу­чаях некоторые или все задачи в программе могут выполняться независимо (или асинхронно). Есть, конечно, и смешанные ситуации.

Термины "программа" и "задача" довольно туманны: программный код теоретически и практически можно разбить на фрагменты по-разному. Программист может сформировать программу путем компиляции и компоновки множества мелких программ или моду­лей. При выполнении такие программы могут вызывать другие программы и подпрограм­мы, часть из которых могут представлять собой системные вызовы - предусмотренные в ОС базовые обслуживающие программы. Поэтому с одной стороны программа - это под­готавливаемый программистом исходный код, с другой - скомпилированный двоичный файл на диске, а с третьей - двоичный код, находящийся в ОЗУ и ожидающий планирования.

Процесс и программа не идентичные понятия. Во-первых, существуют процессы, ко­торым не соответствует никакой программы (т. е. исполняемого файла). Во-вторых, программа в ходе своего выполнения может породить более одного процесса. А в-третьих, при запуске программы порождение нового процесса (системный вызов fork) предшествует собственно запуску программы (системный вызов exec).

С технической точки зрения, образ программы представляет собой выполняемую программу в двоичном виде (которую не совсем верно называют сегментом текста), а также различные структуры данных, содержащие контекст программы, и две ее рабочих области - сегмент данных и стек. Упрощенная версия образа программы UNIX показана на рис.59.

 

Рис. 59. Схема образа программы

 

Когда вызывается какая-либо команда, командный процессор загружает образ программы (через вызов ядра) и начинает новый процесс с уникальным числовым идентифи­катором процесса (PID).Все последующие ссылки на этот процесс (как пользователей, так и других процессов) осуществляется через идентификатор процесса, а не имя исходной команды. Идентификаторы процессов 0 и 1 резервируются для специальных процессов, создаваемых при загрузке.

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

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

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

Неинтерактивные процессы именуются обычно демонами (daemon - Disk And Execution MONitor). Они инициируются самой системой при ее загрузке обычным образом (запуском соответствующих программ) и функционируют в фоновом режиме, активизируясь при требовании соответ­ствующей функции (печати, доставки почты и т. д.). Не будучи при этом привязанными к какому-либо терминалу, реальному или виртуальному.

Каждый выполняемый процесс будет выбираться для выполнения согласно своему приоритету, который может устанавливаться и изменяться. Обычно процесс выполняется до завершения, а затем прекращает свое существование. Процесс может также завершить­ся в результате возникновения какой-то ошибки или сигнала (такого как kill)от пользова­теля или другого процесса. При завершении процесса он удаляется из таблицы процессов, а занятая им ОП освобождается для новых процессов.

Когда работает процесс А, он может порождать другой процесс, процесс В. Говорят, что процесс В является дочерним процессом процесса А, а процесс А называется родительским процессом. Помимо собственного идентификатора, каждый процесс имеет атри­бут PPID,то есть идентификатор родительского процесса.

Атрибутами процесса являются:

- идентификатор процесса и его родителя;

- его приоритет;

- реальный и эффективный идентификаторы пользователя;

- реальный и эффективный идентификаторы группы пользователей.

Для получения информации о протекающих в системе процессах и их атри­бутах служит команда ps.