Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Системное программное обеспечение

.pdf
Скачиваний:
68
Добавлен:
01.05.2014
Размер:
444.39 Кб
Скачать

Эти имена соответствуют положительным числам. Например, сигнал 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