Системное программное обеспечение
.pdfЭти имена соответствуют положительным числам. Например, сигнал SIGINT обычно определяется так:
#define SIGINT 2
Посланный в процесс сигнал обрабатывается. Процесс может отреагировать на ожидающий обработки сигнал одним из трех способов:
Выполнить действие, предусмотренное по умолчанию. Для большинства сигналов это означает завершение процесса.
Проигнорировать сигнал. Сигнал отбрасывается и никакого эффекта на процесс получатель не оказывает.
Вызвать функцию, определенную пользователем. Эту функцию называют программой-обработчиком сигналов. Когда такая функция вызывается, го ворят, что сигнал перехватывается. Если эта функция заканчивает свое вы полнение без завершения процесса, то процесс продолжает выполняться с
той точки, на которой он был прерван сигналом.
Процесс может создавать для каждого сигнала индивидуальный механизм обра ботки. Например, одни сигналы он может игнорировать, некоторые перехватывать, а с остальными выполнять действие, предусмотренное по умолчанию. Боле того, в ходе вы полнения процесс может изменять принцип обработки определенных сигналов.
Условия генерации некоторых наиболее часто используемых сигналов и действие систе мы по умолчанию приведены в таблице 6.1.
|
|
Таблица 6.1 |
|
|
|
Название |
Действие по |
Значение |
|
умолчанию |
|
SIGALRM |
Завершить |
Сигнал отправляется, когда срабатывает таймер, ранее |
|
|
установленный с помощью системных вызовов |
|
|
alarm(2) или setitimer(2). |
SIGCHLD |
Игнорировать |
Сигнал, посылаемый родительскому процессу при за |
|
|
вершении выполнения его потомка. |
SIGILL |
Завершить |
Сигнал посылается ядром, если процесс попытался вы |
|
|
полнить недопустимую инструкцию. |
SIGINT |
Завершить |
Сигнал посылается ядром всем процессам текущей |
|
|
группы при нажатии клавиши прерывания (<Del> или |
|
|
<Ctrl> + <C>). |
SIGKILL |
Завершить |
Сигнал, при получении которого выполнение процесса |
|
|
завершается. Этот сигнал нельзя ни перехватить, ни |
|
|
игнорировать. |
SIGPOLL |
Завершить |
Сигнал отправляется при наступлении определенного |
|
|
события для устройства, которое является опрашивае |
|
|
мым. |
SIGQUIT |
Завершить |
Сигнал посылается ядром всем процессам текущей |
|
|
группы при нажатии клавиш <Ctrl> + <I>. |
SIGSTOP |
Остановить |
Сигнал отправляется всем процессам текущей группы |
|
|
при нажатии пользователем клавиш <Ctrl> + <Z>. |
|
|
Получение сигнала вызывает остановку выполнения |
21
Название |
Действие по |
Значение |
|
умолчанию |
|
|
|
процесса. |
SIGTERM |
Завершить |
Сигнал обычно представляет своего рода предупре |
|
|
ждение, что процесс вскоре будет уничтожен. |
SIGUSR1 |
Завершить |
Сигнал предназначен для прикладных задач как про |
|
|
стейшее средство межпроцессного взаимодействия. |
SIGUSR2 |
Завершить |
Сигнал предназначен для прикладных задач как про |
|
|
стейшее средство межпроцессного взаимодействия. |
6.3. Программные каналы
Если два или несколько процессов совместно выполняют одну и ту же задачу, то они неизбежно должны использовать общие данные. Хотя сигналы и могут быть полезны для синхронизации процессов или для обработки исключительных ситуаций или ошибок, они совершенно не подходят для передачи данных от одного процесса к другому. Один из воз можных путей разрешения этой проблемы заключается в совместном использовании файлов, так как ничто не мешает нескольким процессам одновременно выполнять опера ции чтения или записи для одного и того же файла. Тем не менее совместный доступ к файлам может оказаться неэффективным и потребует специальных мер предосторожности для избежания конфликтов.
Для решения этих проблем стандарт POSIX.1 обеспечивает конструкцию, которая называ ется программными каналами (pipe). Программный канал (или просто канал) служит для установления односторонней связи, соединяющей один процесс с другим.
Каналы создаются в программе при помощи системного вызова pipe. В случае успешного завершения вызов сообщает два дескриптора файла: один для записи в канал, а другой для чтения из него. Вызов pipe определяется следующим образом:
# include <unistd.h> int pipe (int filedes[2]);
Переменная filedes является массивом из двух целых чисел, который будет содержать дескрипторы файлов, обозначающие канал. После успешного вызова filedes[0] будет открыт для чтения из канала, а filedes[1] для записи в канал. В случае неудачи вызов pipe вернёт значение –1.
После создания канала с ним можно работать просто при помощи вызовов read и write.
6.4. Именованные каналы, или FIFO
Каналы являются изящным и мощным механизмом межпроцессного взаимодействия. Тем не менее они имеют ряд недостатков.
Первый, и наиболее серьёзный из них, заключается в том, что каналы могут использовать ся только для связи процессов, имеющих общее происхождение, таких как родительский процесс и его потомок. Это ограничение становится очевидным при попытке разработать настоящую «серверную» программу, которая выполняется постоянно, обеспечивая си стемный сервис. Примерами таких программ являются серверы управления сетью и спу леры печати. В идеале клиентские процессы должны иметь возможность стартовать, под ключаться к не связанному с ним серверному процессу при помощи канала, а затем снова
22
отключаться от него. К сожалению, такую модель при помощи обычных каналов реализо вать нельзя.
Второй недостаток каналов заключается в том, что они не могут существовать постоянно. Они каждый раз должны создаваться заново, а после завершения обращающегося к ним процесса уничтожаются.
Для восполнения этих недостатков существует разновидность канала, называемая имено ванным каналом, или файлом типа FIFO (сокращение от first-in first-out, то есть «первым вошёл/первый вышел»). FIFO очень похожи на каналы, поскольку являются однонаправ ленным средством передачи данных, Однако в отличие от программных каналов, FIFO имеют имена, которые позволяют независимым процессам получить доступ к этим объек там.
Для создания FIFO используется системный вызов mknod: int mknod(char *pathname, int mode, int dev);
где pathname – имя файла в файловой системе (имя FIFO), mode – флаги владения, прав доступа и т.д., dev – при создании FIFO игнорируется.
6.5. Очереди сообщений
Очереди сообщений размещаются в адресном пространстве ядра и являются разделяемым системным ресурсом. Каждая очередь сообщений имеет свой уникальный идентификатор. Процессы могут записывать и считывать сообщения из различных очередей. Процесс по славший сообщение в очередь, может не ожидать чтения этого сообщения каким либо другим процессом. Он может закончить своё выполнение, оставив в очереди сообщение, которое будет прочитано другим процессом позже.
Когда процесс передаёт сообщение в очередь, ядро создаёт для него новую запись и поме щает её в конец. связанного списка записей, соответствующих сообщениям указанной очереди. В каждой такой записи указывается тип сообщения, число байтов данных, содер жащихся в сообщении и указатель на другую область данных ядра, где фактически нахо дятся данные сообщения. Ядро копирует данные, содержащиеся в сообщении, из адресно го пространства процесса-отправителя в эту область данных ядра, чтобы процесс-отправи тель мог завершиться, а сообщение осталось доступным для чтения другими процессами. Когда процесс выбирает сообщение из очереди, ядро копирует относящиеся к нему дан ные из записи сообщения в адресное пространство этого процесса, а затем удаляет запись. Процесс может выбрать сообщение из очереди различными способами. Например, он мо жет выбрать:
самое старое сообщение, независимо от его типа;
сообщение, идентификатор (тип) которого совпадает с идентификатором,
указанным процессом; Если процесс попытается прочитать сообщение из очереди, в которой ни одно сооб
щение не удовлетворяет критерию поиска, ядро по умолчанию переведёт его в состояние ожидания (пока в очередь не поступит сообщение, которое этот процесс сможет прочи тать). Если же процесс укажет в системном вызове приёма сообщения неблокирующий флаг, то этот вызов не заблокирует процесс, а выдаст код неудачного завершения.
23
6.6. Семафоры
Для синхронизации процессов, а точнее для синхронизации доступа нескольких процес сов к разделяемым ресурсам, используются семафоры. Семафор (S) – это целая неотрица тельная переменная, которая доступна параллельным процессам для проведения над ней только двух видов операций - «закрытия» и «открытия» - названных соответственно P и V операциями. Операции определены следующим образом.
P(S)
if (S>0)
уменьшить s на единицу; else
ждать, пока S не станет ненулевым, затем вычесть единицу. V(S)
увеличить S на единицу;
if (очередь ожидающих процессов не пуста)
продолжить выполнение первого процесса в очереди ожидания.
Семафоры могут использоваться несколькими способами. Наиболее простой из них за ключается в обеспечении взаимного исключения, когда только один процесс может вы полнять определенный участок кода одновременно. Рассмотрим схему следующей про граммы.
P(S);
какие-либо действия; V(S);
Предположим, что начальное значение семафора S равно единице. Тогда из определений операций P и V следует, что:
(число завершенных операций P – число завершенных операций V) ≤ 1
Другими словами, в каждый момент времени только один процесс может выполнять груп пу операторов, заключенных между операциями P и V.
Для успешной работы с семафорами необходимо обеспечить выполнение следующих условий:
1.Значение семафора должно быть доступно различным процессам. Поэтому сема фор находится не в адресном пространстве процесса, а в пространстве ядра.
2.Операция проверки и изменения значения семафора должна быть реализована в виде атомарной по отношению к другим процессам (т.е. непрерывной другими процессами) операции. В противном случае возможна ситуация, когда после про верки значения семафора выполнение процесса будет прервано другим процес сом, который в свою очередь проверит семафор и изменит его значение. Единственным способом гарантировать атомарность критических участков опе рации является выполнение этих операций в режиме ядра.
6.7.Мьютексы
Одним из вариантов семафорных механизмов для организации взаимного исключе ния являются так называемые мьютексы (mutex). Термин mutex произошел от английского словосочетания mutual exclusion semaphore, что дословно и переводится как семафор вза
24
имного исключения. Мьютексы реализованы во многих ОС, их основное назначение – ор ганизация взаимного исключения для потоков из одного и того же или из разных процес сов. Мьютексы – это простейшие двоичные семафоры, которые могут находиться в одном из двух состояний – отмеченном или неотмеченном (открыт и закрыт соответственно). Когда какой-либо поток, принадлежащий любому процессу, становится владельцем объекта mutex, последний переводится в неотмеченное состояние. Если задача освобожда ет мьютекс, его состояние становится отмеченным.
Организация последовательного доступа к ресурсам с использованием мьютексов становится несложной, поскольку в каждый конкретный момент только один поток может владеть этим объектом. Для того, чтобы объект mutex стал доступен потокам, принадле жащим разным процессам, при создании ему необходимо присвоить имя. Потом это имя нужно передать «по наследству» задачам, которые должны его использовать для взаимо действия. Для этого вводятся специальные системные вызовы (CreateMutex), в которых указывается начальное значение мьютекса и его имя.
Для работы с мьютексом имеется несколько функций. Помимо уже упомянутой функции создания такого объекта (CreateMutex), есть функции открытия (OpenMutex) и функция освобождения этого объекта (ReaeaseMutex).
Конкретные обращения к этим функциям и перечни передаваемых и получаемых параметров нужно смотреть в документации на соответствующую ОС.
6.8. Разделяемая память
Разделяемая память позволяет множеству процессов отображать часть своих вирту альных адресов в общую область памяти. Благодаря этому любой процесс может записы вать данные в разделяемую область памяти, и эти данные будут доступны для чтения и модификации другим процессам.
Разделяемая память была задумана как средство повышения производительности при обработке сообщений. Дело в том, что при передачи сообщения из процесса в очередь сообщений данные копируются из буфера передающего процесса в буфер ядра и затем в буфер принимающего процесса. Механизм разделяемой памяти позволяет избавиться от накладных расходов передачи данных через ядро, предоставляя двум или более процессам возможность непосредственного получения доступа к одной области памяти для обмена данными. При этом, однако. В разделяемой памяти не предусмотрены какие-либо методы управления доступом процессов, которые ею пользуются. Поэтому среда межпроцессного взаимодействия формируется, как правило, путём использования разделяемой памяти и семафоров.
Примерный сценарий работы с разделяемой памятью выглядит следующим об
разом:
1.Сервер получает доступ к разделяемой памяти, используя семафор.
2.Сервер производит запись данных в разделяемую память.
3.После завершения записи сервер освобождает разделяемую память с помощью семафора.
4.Клиент получает доступ к разделяемой памяти, запирая ресурс с помощью сема фора.
25
5. Клиент производит чтение данных из разделяемой памяти и освобождает её, ис пользуя семафор
Аналогично сообщениям и семафорам, разделяемая память располагается в адрес ном пространстве ядра, и разделяемые области не освобождаются. Даже если создавшие их процессы завершаются. В любой момент времени в системе может существовать мно жество разделяемых областей памяти.
26
7. ПОДСИСТЕМА УПРАВЛЕНИЯ ПАМЯТЬЮ ВС
Одной из основных функций ОС является эффективное управление памятью. Опе ративная память (ОП), или основная память, или память с произвольным доступом (Ran dom Access Memory RAM) является достаточно дорогостоящим ресурсом. Время доступа к ОП составляет всего несколько циклов процессора, поэтому работа с данными находя щимися в памяти, обеспечивает максимальную производительность. К сожалению, дан ный ресурс, как правило, ограничен. Поэтому данные, которые не могут быть размещены в ОП, располагаются на вторичных устройствах хранения, или во вторичной памяти, роль которой обычно выполняют дисковые накопители. Время доступа ко вторичной памяти на несколько порядков превышает время доступа к оперативной памяти и требует активного содействия ОС. Подсистема управления памятью ОС отвечает за справедливое и эффек тивное распределение разделяемого ресурса ОП между процессами и за обмен данными между оперативной и вторичной памятью.
Можно сформулировать ряд возможностей. Которые должна обеспечивать подси стема управления памятью современной многозадачной ОС.
Выполнение задач, размер которых превышает размер ОП.
Выполнение частично загруженных в память задач, для минимизации вре мени их запуска.
Размещение нескольких задач в памяти одновременно. Для повышения эф фективности использования процессора.
Размещение задач в произвольном месте ОП.
Совместное использование несколькими задачами одних и тех же областей памяти.
Размещение задачи в нескольких различных частях ОП.
Оперативная память является особым ресурсом. Поскольку её распределение долж но проходить не изолировано, а согласованно с распределением центрального процессора. Нужно стремиться выбирать не только «хороший» по некоторым критериям алгоритм рас пределения процессора, но и соответствующий ему алгоритм распределения ОП, с тем чтобы обеспечить работу машины с требуемыми характеристиками в том или ином вари анте мультипрограммной работы.
7.1. Понятие виртуальной памяти
Оперативная память может быть представлена в виде последовательности байтов, каждый из которых имеет свой уникальный адрес, называемый физическим» адресом. Именно эти адреса, в конечном счете использует процессор, обмениваясь данными с опе ративной памятью. Однако адресное пространство процесса существенным образом отли чается от адресного пространства физической ОП.
Если бы адресное пространство процесса непосредственно отображалось бы в опе ративную память, т.е. адреса, используемые процессом, являлись бы физическими адреса ми, то на пути создания многозадачной системы возник бы ряд непреодолимых препят ствий.
Во-первых, отсутствует механизм, защищающий адресное пространство одного процесса, от адресного пространства другого или, что более важно,
27
от адресного пространства самой оперативной системы. Поскольку каждый процесс работает с физическими адресами, нет никакой гарантии, что он не обратится к ячейкам памяти, принадлежащим другим процессам или ядру системы.
Во-вторых, уже на этапе компиляции необходимо было бы предусмотреть распределение существующего физического адресного пространства таким образом, чтобы при запуске каждый процесс занимал непрерывную и не пересекающуюся область физических адресов.
В-третьих, объем физической оперативной памяти будет существенным об
разом ограничивать число процессов, одновременно выполняющихся в си стеме.
Все перечисленные проблемы преодолимы с помощью виртуальной памяти. При этом адреса, используемые приложениями и самим ядром, не обязаны соответствовать физическим адресам. Виртуальные адреса транслируются или отображаются в физические на аппаратном уровне при активном участии ядра ОС.
Смысл виртуальной памяти заключается в том, что каждый процесс выполняется в собственном виртуальном адресном пространстве (ВАП). При этом он больше не ограни чен объемом физической памяти – виртуальная память может значительно превышать фи зическую. В результате процессы становятся изолированными друг от друга и не имеют возможности «хозяйничать» в адресном пространстве соседа. Физическая память распре деляется максимально эффективно – она не зависит от распределения виртуальной памяти отдельного процесса.
ВАП процесса, как правило, является последовательным, тогда как расположение соответствующих областей физической памяти может иметь фрагментированный харак тер, позволяя оптимально распределять память между процессами.
Размер ВП может существенно превышать размер физической за счет использова ния вторичной памяти – как правило, дискового пространства, где могут сохраняться вре менно не используемые участки адресного пространства процесса.
7.2. Методы реализации виртуальной памяти
7.2.1 Простое непрерывное распределение и распределение с перекрытием (оверлейные структуры)
Простое непрерывное распределение – это самая простая схема реализации вирту альной памяти, согласно которой вся память условно может быть разделена на три части:
область занимаемая ОС;
область, в которой размещается исполняемая задача;
свободная область памяти.
Изначально являясь самой первой схемой, она продолжает и сегодня быть доста точно распространённой. Эта схема предполагает, что ОС не поддерживает мультипро граммирование, поэтому не возникает проблемы распределения памяти между нескольки ми задачами. Программные модули, необходимые для всех программ располагаются в об ласти самой ОС, а вся оставшаяся память может быть предоставлена только одной задаче. В этом случае область памяти получается непрерывной.
28
Поскольку в различных вычислительных комплексах для системных нужд могут быть отведены отличающиеся объёмы оперативной памяти, то виртуальные адреса про граммы не привязываются жёстко к физическому адресному пространству. Эта привязка осуществляется на этапе загрузки задачи в память.
Если есть необходимость создать программу, виртуальное адресное пространство которой должно быть больше чем свободная область памяти, или даже больше, чем весь возможный объём оперативной памяти, то используется распределение с перекрытием (т.н. оверлейные структуры, overlay – перекрытие). Этот метод распределения предполага ет, что вся программа может быть разделена на части – сегменты. Каждая оверлейная про грамма имеет одну главную часть и несколько сегментов, причём в памяти машины од новременно могут находиться только её главная часть и один или несколько не перекры вающихся сегментов.
Пока в оперативной памяти располагаются выполняющиеся сегменты, остальные находятся во вторичной памяти. После того, как текущий сегмент завершит своё выполне ние возможны два варианта. Либо он сам обращается к ОС с указанием, какой сегмент должен быть загружен в память следующим. Либо он возвратит управление главному сег менту задачи и уже тот обращается к ОС с указанием, какой сегмент должен быть загру жен в память и вновь отдаёт управление одному из сегментов.
7.2.2 Распределение статическими и динамическими разделами
Для организации мультипрограммного режима необходимо обеспечить одновре менное расположение в оперативной нескольких задач. Самая простая схема распределе ния памяти между несколькими задачами предполагает, что память незанятая ядром ОС может быть разбита на несколько непрерывных частей (разделов). Разделы характеризу ются именем, типом, границами.
Разбиение памяти на несколько непрерывных разделов может быть фиксированным (статическим) либо динамическим (то есть процесс выделения нового раздела памяти происходит непосредственно при появлении новой задачи).
Разделы с фиксированными границами
Разбиение всего объёма оперативной памяти на несколько разделов может осуще ствляться единовременно (то есть в процессе установки ОС) или по мере необходимости оператором системы.
В каждом разделе в каждый момент времени может располагаться по одной про грамме (задаче). В этом случае по отношению к каждому разделу можно применить все те методы создания программ, которые используются для однопрограммных систем.
Возможно использование оверлейных структур. Первые мультипрограммные ОС строились по этой схеме. Использовалась эта схема и много лет спустя при создании недорогих вычислительных систем.
29
Ядро ОС |
|
|
|
раздел 0 |
|
Транзитная |
||
|
||
область ОС |
|
|
Задача А |
|
|
|
раздел 1 |
|
неиспользуем |
||
|
||
ая область |
|
|
|
|
|
Задача В |
|
|
|
раздел 2 |
|
неиспользуем |
|
|
ая область |
|
|
Задача С |
|
|
неиспользуем |
раздел 3 |
|
ая область |
|
|
|
|
Рис. 7.1. Распределение памяти разделами с фиксированными границами
Основным недостатком такого способа распределения памяти является наличие до статочно большого объёма неиспользуемого памяти. Поскольку разделов несколько, то и неиспользуемых областей получается несколько, такие потери оперативной памяти стали называть фрагментацией памяти.
Желание разработчиков сократить столь значительные потери привело их к сле-дующим двум решениям:
выделить раздел ровно такого объёма, который нужен под текущую задачу;
размещать задачу не в одной непрерывной области памяти, а в нескольких областях.
Разделы с подвижными границами
Чтобы избавиться от фрагментации, можно попробовать размещать в оперативной памяти задачи плотно, одну за другой, выделяя ровно столько памяти, сколько задача тре бует. Специальный планировщик (диспетчер памяти) ведёт список адресов свободной оперативной памяти. При появлении новой задачи диспетчер памяти просматривает этот список и выделяет для задачи раздел, объём которого либо равен необходимому, либо несколько больше, если память выделяется не ячейками, а некими дискретными единица
30