Н.В. Вдовикина, А.В. Казунин, И.В. Машечкин, А.Н. Терехин - Системное программное обеспечение - взаимодействие процессов (2002), страница 5
Описание файла
Документ из архива "Н.В. Вдовикина, А.В. Казунин, И.В. Машечкин, А.Н. Терехин - Системное программное обеспечение - взаимодействие процессов (2002)", который расположен в категории "". Всё это находится в предмете "операционные системы" из 3 семестр, которые можно найти в файловом архиве МГУ им. Ломоносова. Не смотря на прямую связь этого архива с МГУ им. Ломоносова, его также можно найти и в других разделах. .
Онлайн просмотр документа "Н.В. Вдовикина, А.В. Казунин, И.В. Машечкин, А.Н. Терехин - Системное программное обеспечение - взаимодействие процессов (2002)"
Текст 5 страницы из документа "Н.В. Вдовикина, А.В. Казунин, И.В. Машечкин, А.Н. Терехин - Системное программное обеспечение - взаимодействие процессов (2002)"
typedef int semaphore; /* тип данных «семафор» */
semaphore customers = 0; /* посетители, ожидающие в очереди */
semaphore barbers = 0; /* парикмахеры, ожидающие посетителей */
semaphore mutex = 1; /* контроль за доступом к переменной waiting */
int waiting = 0;
void barber()
{
while (true) {
down(customers); /* если customers == 0, т.е. посетителей нет, то заблокируемся до появления посетителя */
down(mutex); /* получаем доступ к waiting */
waiting = wating – 1; /* уменьшаем кол-во ожидающих клиентов */
up(barbers); /* парикмахер готов к работе */
up(mutex); /* освобождаем ресурс waiting */
cut_hair(); /* процесс стрижки */
}
void customer()
{
down(mutex); /* получаем доступ к waiting */
if (waiting < CHAIRS) /* есть место для ожидания */
{
waiting = waiting + 1; /* увеличиваем кол-во ожидающих клиентов */
up(customers); /* если парикмахер спит, это его разбудит */
up(mutex); /* освобождаем ресурс waiting */
down(barbers); /* если парикмахер занят, переходим в состояние ожидания, иначе – занимаем парикмахера*/
get_haircut(); /* процесс стрижки */
}
else
{
up(mutex); /* нет свободного кресла для ожидания – придется уйти */
}
}
Часть II. реализация процессов.
В этой части пособия мы подробно рассмотрим практическое применение понятия процесса в ОС, а также реализацию механизма управления процессами на примере ОС UNIX.
4Реализация процессов в ОС UNIX
4.1Понятие процесса в UNIX.
Выше уже говорилось, что в каждой конкретной ОС существует свое системно-ориентированное определение понятия процесса. В ОС UNIX процесс можно определить, с одной стороны, как единицу управления и потребления ресурсов, с другой стороны – как объект, зарегистрированный в таблице процессов ядра UNIX. Каждому процессу в UNIX сопоставлено некое уникальное целое число, называемое идентификатором процесса – PID. Это число находится в диапазоне от нуля до некоторого предельного номера, характеризующего максимально возможное количество одновременно существующих процессов в данной ОС. Некоторые значения идентификаторов являются зарезервированными и назначаются специальным процессам ОС, например, процесс с PID=0 ассоциируется с работой ядра ОС, а процесс с PID=1 – это процесс init, работа которого будет подробно рассмотрена ниже.
4.1.1Контекст процесса.
С точки зрения организации данных ядра ОС, идентификатор процесса фактически представляет собой номер записи в таблице процессов, соответствующей данному процессу. Содержимое записи таблицы процессов позволяет получить доступ к контексту процесса (а именно, часть информации, составляющей контекст процесса, хранится непосредственно в таблице процессов, а на структуры данных, содержащие оставшуюся часть контекста, в записи таблицы процессов имеются прямые или косвенные ссылки). Таблица процессов поддерживается ядром UNIX и находится в адресном пространстве ядра.
С точки зрения логической структуры контекст процесса в UNIX состоит из:
-
пользовательской составляющей или тела процесса (иногда используется термин «пользовательский контекст»)
-
аппаратной составляющей (иногда используется термин «аппаратный контекст»)
-
системной составляющей ОС UNIX (иногда называемой «системным контекстом» или «контекстом системного уровня»)
Иногда при рассмотрении контекста процесса два последних компонента объединяют, в этом случае используется термин общесистемная составляющая контекста.
Рассмотрим подробнее каждую из составляющих контекста процесса.
Рис. 6 Контекст процесса
4.1.2Тело процесса.
Тело процесса состоит из сегмента кода и сегмента данных3.
Сегмент кода содержит машинные команды и неизменяемые константы соответствующей процессу программы. Данные в этом сегменте не подлежат изменению.
Сегмент данных содержит данные, динамически изменяемые в ходе выполнения процесса. Сегмент данных содержит область статических переменных, область разделяемой с другими процессами памяти, а также область стека (обычно эта область служит основой для организации автоматических переменных, передачи параметров в функции, организацию динамической памяти).
Некоторые современные ОС имеют возможность разделения единого сегмента кода между разными процессами. Тем самым достигается экономия памяти в случаях одновременного выполнения идентичных процессов. Например, при функционировании терминального класса одновременно могут быть сформированы несколько копий текстового редактора. В этом случае сегмент кода у всех процессов, соответствующих редакторам, будет единый, а сегменты данных будут у каждого процесса свои.
Рис. 7 Разделение сегмента кода разными экземплярами программы
Следует отметить, что при использовании динамически загружаемых библиотек возможно разделение сегмента кода на неизменную часть, которая может разделяться между процессами и часть, соответствующую изменяемому в динамике коду подгружаемых программ.
4.1.3Аппаратный контекст.
Аппаратная составляющая содержит все регистры и аппаратные таблицы ЦП, используемые активным или исполняемым процессом (счетчик команд, регистр состояния процессора, аппарат виртуальной памяти, регистры общего назначения и т. д.). Аппаратная составляющая контекста доступна только в тот момент, когда процесс находится в состоянии выполнения. В тот момент, когда процесс переходит из состояния выполнения в какое-либо другое состояние, необходимые данные из аппаратной составляющей его контекста сохраняются в область системной составляющей контекста, и до тех пор, пока процесс снова не перейдет в состояние выполнения, в его контексте аппаратной составляющей не будет.
4.1.4Системный контекст.
В системной составляющей контекста процесса содержатся различные атрибуты процесса, такие как:
-
идентификатор родительского процесса
-
текущее состояние процесса
-
приоритет процесса
-
реальный и эффективный идентификатор пользователя-владельца
-
реальный и эффективный идентификатор группы, к которой принадлежит владелец
-
список областей памяти
-
таблица открытых файлов процесса
-
диспозиция сигналов, т.е. информация о том, какая реакция установлена на тот или иной сигнал
-
информация о сигналах, ожидающих доставки в данный процесс
-
сохраненные значения аппаратной составляющей (когда выполнение процесса приостановлено)
Поясним сказанное выше.
Реальные и эффективные идентификаторы пользователя и группы. Как правило при формировании процесса эти идентификаторы совпадают и равны реальному идентификатору пользователя и реальному идентификатору группы, т.е. они определяются персонификацией пользователя, сформировавшего данный процесс. При этом права процесса по доступу к файловой системе определяются правами сформировавшего процесс пользователя и его группы. Этого бывает недостаточно. Примером может служить ситуация, когда пользователь желает запустить некоторый процесс, изменяющий содержимое файлов, которые не принадлежать этому пользователю (например, изменение пароля на доступ пользователя в систему). Для разрешения данной ситуации имеется возможность установить специальный признак в исполняемом файле, наличие которого позволяет установить в процессе, сформированном при запуске данного файла в качестве эффективных идентификаторов, идентификатор владельца и группы владельца этого файла.
Рис. 8 Формирование контекста процесса.
4.2Аппарат системных вызов в OC UNIX.
Как известно, одной из основных функций любой ОС является управление ресурсами. Вынесение непосредственного доступа к ресурсам в зону ответственности ядра необходимо для того, чтобы обеспечить надежность и работоспособность всей вычислительной системы, так как невозможно гарантировать, что пользовательский процесс, получив непосредственный доступ к ресурсам вычислительной системы, будет работать с ними корректно. Кроме того, в многозадачной системе имеет место конкуренция процессов за ресурсы, и ОС должна здесь выполнять также функцию планирования доступа к ресурсам и защиты ресурсов, выделенных конкретному процессу, от несанкционированного доступа со стороны других процессов.
Чтобы обеспечить гарантии того, что определенные действия, такие как операции с ресурсами, планирование процессов и т.п., может выполнять только ОС, вычислительная система должна обладать определенными свойствами, и в частности, иметь привилегированный режим выполнения. Это означает, что в ВС имеется два режима выполнения: обычный (пользовательский) и привилегированный (иногда называемый также режимом ядра, или защищенным режимом). Существует набор операций (инструкций), которые не могут быть выполнены процессом, работающим в пользовательском режиме. Они доступны только в привилегированном режиме, в котором работает ядро ОС. Кроме того, процессу, работающему в пользовательском режиме, недоступно адресное пространство других процессов и адресное пространство ядра.
Итак, обычные процессы выполняются в пользовательском режиме, и им недоступны те операции, которые может выполнять ядро ОС, работающее в привилегированном режиме, в частности, непосредственный доступ к ресурсам. Каким же образом обычный процесс, работающий в пользовательском режиме, может все же получить возможность работать с ресурсами ВС, например, записывать данные в файл или выводить их на печать? Для обеспечения такой возможности вводится аппарат системных вызовов, посредством которых ядро предоставляет процессам определенный набор услуг.
С точки зрения пользовательского процесса, системные вызовы оформлены аналогично библиотечным функциям, и обращение к ним при программировании ничем не отличается от вызова обычной функции. Однако в действительности при обращении к системному вызову выполнение переключается в привилегированный режим, благодаря чему во время выполнения системного вызова процессу доступны все инструкции, в том числе и привилегированные, а также системные структуры данных. По завершении выполнения системного вызова выполнение процесса снова переключается в пользовательский режим. Таким образом, механизм системных вызовов, код которых является частью ядра, является для обычного пользовательского процесса единственной возможностью получить права для выполнения привилегированных операций, и тем самым обеспечивается безопасность системы в целом.
Так как любой процесс может в различные моменты своего выполнения находиться как в привилегированном режиме, так и в пользовательском режиме, то и виртуальное адресное пространство процесса состоит из двух частей: одна из них используется, когда процесс находится в пользовательском режиме, а другая – в привилегированном. Причем процессу, находящемуся в пользовательском режиме, недоступна та часть его виртуального адресного пространства, которая соответствует режиму ядра. На Рис. 8 показано отображение исполняемого файла на виртуальное адресное пространство процесса, которое производит ОС при запуске процесса.
Далее нами будут рассмотрены некоторые системные вызовы, предоставляемые ОС UNIX. К интересующим нас вызовам относятся вызовы
-
для создания процесса;
-
для организации ввода вывода;
-
для решения задач управления;
-
для операции координации процессов;
-
для установки параметров системы.
Отметим некоторые общие моменты, связанные с работой системных вызовов.
Большая часть системных вызовов определены как функции, возвращающие целое значение, при этом при нормальном завершении системный вызов возвращает 0, а при неудачном завершении -14. При этом код ошибки можно выяснить, анализируя значение внешней переменной errno, определенной в заголовочном файле <errno.h>.
В случае, если выполнение системного вызова прервано сигналом, поведение ОС зависит от конкретной реализации. Например, в BSD UNIX ядро автоматически перезапускает системный вызов после его прерывания сигналом, и таким образом, внешне никакого различия с нормальным выполнением системного вызова нет. Стандарт POSIX допускает и вариант, когда системный вызов не перезапускается, при этом системный вызов вернет –1, а в переменной errno устанавливается значение EINTR, сигнализирующее о данной ситуации.
4.3Порождение новых процессов.
Для порождения новых процессов в UNIX существует единая схема, с помощью которой создаются все процессы, существующие в работающем экземпляре ОС UNIX, за исключением процессов с PID=0 и PID=15.
Для создания нового процесса в операционной системе UNIX используется системный вызов fork().
#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);
При этом в таблицу процессов заносится новая запись, и порожденный процесс получает свой уникальный идентификатор. Для нового процесса создается контекст, большая часть содержимого которого идентична контексту родительского процесса, в частности, тело порожденного процесса содержит копии сегментов кода и данных его родителя. Кроме того, в порожденном процессе наследуется (т.е. является копией родительской):
- окружение - при формировании процесса ему передается некоторый набор параметров-переменных, используя которые, процесс может взаимодействовать с операционным окружением (интерпретатором команд и т.д.);